ncurses 5.7 - patch 20090214
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2007,2008 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.336 2009/01/25 00:39:14 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 #ifdef CURSES_WACS_ARRAY
110 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
111 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
112 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
113 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
114 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
115 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
116 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
117 #endif
118
119 #endif
120
121 #if HAVE_WCSRTOMBS
122 #define count_wchars(src, len, state)      wcsrtombs(0,   &src, len, state)
123 #define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
124 #define reset_wchars(state) memset(&state, 0, sizeof(state))
125 #elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
126 #define count_wchars(src, len, state)      wcstombs(0,   src, len)
127 #define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
128 #define reset_wchars(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
129 #define state_unused
130 #endif
131
132 #if HAVE_MBSRTOWCS
133 #define count_mbytes(src, len, state)      mbsrtowcs(0,   &src, len, state)
134 #define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
135 #define reset_mbytes(state) memset(&state, 0, sizeof(state))
136 #elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
137 #define count_mbytes(src, len, state)      mbstowcs(0,   src, len)
138 #define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
139 #define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
140 #define state_unused
141 #endif
142
143 #define ToggleAcs(temp,real) temp = ((temp == real) ? 0 : real)
144
145 #define P(string)       printw("%s\n", string)
146
147 #define BLANK           ' '     /* this is the background character */
148
149 #undef max_colors
150 static int max_colors;          /* the actual number of colors we'll use */
151 static int min_colors;          /* the minimum color code */
152 static bool use_colors;         /* true if we use colors */
153
154 #undef max_pairs
155 static int max_pairs;           /* ...and the number of color pairs */
156
157 typedef struct {
158     short red;
159     short green;
160     short blue;
161 } RGB_DATA;
162
163 static RGB_DATA *all_colors;
164
165 static void main_menu(bool);
166
167 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
168  * though we can rely on negative x/y values to stop the macro.
169  */
170 static void
171 do_h_line(int y, int x, chtype c, int to)
172 {
173     if ((to) > (x))
174         mvhline(y, x, c, (to) - (x));
175 }
176
177 static void
178 do_v_line(int y, int x, chtype c, int to)
179 {
180     if ((to) > (y))
181         mvvline(y, x, c, (to) - (y));
182 }
183
184 static void
185 Repaint(void)
186 {
187     touchwin(stdscr);
188     touchwin(curscr);
189     wrefresh(curscr);
190 }
191
192 static bool
193 isQuit(int c)
194 {
195     return ((c) == QUIT || (c) == ESCAPE);
196 }
197 #define case_QUIT       QUIT: case ESCAPE
198
199 /* Common function to allow ^T to toggle trace-mode in the middle of a test
200  * so that trace-files can be made smaller.
201  */
202 static int
203 wGetchar(WINDOW *win)
204 {
205     int c;
206 #ifdef TRACE
207     while ((c = wgetch(win)) == CTRL('T')) {
208         if (_nc_tracing) {
209             save_trace = _nc_tracing;
210             Trace(("TOGGLE-TRACING OFF"));
211             _nc_tracing = 0;
212         } else {
213             _nc_tracing = save_trace;
214         }
215         trace(_nc_tracing);
216         if (_nc_tracing)
217             Trace(("TOGGLE-TRACING ON"));
218     }
219 #else
220     c = wgetch(win);
221 #endif
222     return c;
223 }
224 #define Getchar() wGetchar(stdscr)
225
226 /* replaces wgetnstr(), since we want to be able to edit values */
227 static void
228 wGetstring(WINDOW *win, char *buffer, int limit)
229 {
230     int y0, x0, x, ch;
231     bool done = FALSE;
232
233     echo();
234     getyx(win, y0, x0);
235     wattrset(win, A_REVERSE);
236
237     x = (int) strlen(buffer);
238     while (!done) {
239         if (x > (int) strlen(buffer))
240             x = (int) strlen(buffer);
241         wmove(win, y0, x0);
242         wprintw(win, "%-*s", limit, buffer);
243         wmove(win, y0, x0 + x);
244         switch (ch = wGetchar(win)) {
245         case '\n':
246         case KEY_ENTER:
247             done = TRUE;
248             break;
249         case CTRL('U'):
250             *buffer = '\0';
251             break;
252         case '\b':
253         case KEY_BACKSPACE:
254         case KEY_DC:
255             if (x > 0) {
256                 int j;
257                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
258                     ;
259                 }
260             } else {
261                 beep();
262             }
263             break;
264         case KEY_LEFT:
265             if (x > 0) {
266                 --x;
267             } else {
268                 flash();
269             }
270             break;
271         case KEY_RIGHT:
272             ++x;
273             break;
274         default:
275             if (!isprint(ch) || ch >= KEY_MIN) {
276                 beep();
277             } else if ((int) strlen(buffer) < limit) {
278                 int j;
279                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
280                     buffer[j] = buffer[j - 1];
281                 }
282                 buffer[x++] = (char) ch;
283             } else {
284                 flash();
285             }
286         }
287     }
288
289     wattroff(win, A_REVERSE);
290     wmove(win, y0, x0);
291     noecho();
292 }
293
294 #if USE_WIDEC_SUPPORT
295 static wchar_t
296 fullwidth_of(int ch)
297 {
298     return (ch + 0xff10 - '0');
299 }
300
301 static void
302 make_fullwidth_text(wchar_t *target, const char *source)
303 {
304     int ch;
305     while ((ch = *source++) != 0) {
306         *target++ = fullwidth_of(ch);
307     }
308     *target = 0;
309 }
310
311 static void
312 make_narrow_text(wchar_t *target, const char *source)
313 {
314     int ch;
315     while ((ch = *source++) != 0) {
316         *target++ = ch;
317     }
318     *target = 0;
319 }
320
321 static void
322 make_fullwidth_digit(cchar_t *target, int digit)
323 {
324     wchar_t source[2];
325
326     source[0] = fullwidth_of(digit + '0');
327     source[1] = 0;
328     setcchar(target, source, A_NORMAL, 0, 0);
329 }
330
331 static int
332 wGet_wchar(WINDOW *win, wint_t *result)
333 {
334     int c;
335 #ifdef TRACE
336     while ((c = wget_wch(win, result)) == CTRL('T')) {
337         if (_nc_tracing) {
338             save_trace = _nc_tracing;
339             Trace(("TOGGLE-TRACING OFF"));
340             _nc_tracing = 0;
341         } else {
342             _nc_tracing = save_trace;
343         }
344         trace(_nc_tracing);
345         if (_nc_tracing)
346             Trace(("TOGGLE-TRACING ON"));
347     }
348 #else
349     c = wget_wch(win, result);
350 #endif
351     return c;
352 }
353 #define Get_wchar(result) wGet_wchar(stdscr, result)
354
355 /* replaces wgetn_wstr(), since we want to be able to edit values */
356 static void
357 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
358 {
359     int y0, x0, x;
360     wint_t ch;
361     bool done = FALSE;
362     bool fkey = FALSE;
363
364     echo();
365     getyx(win, y0, x0);
366     wattrset(win, A_REVERSE);
367
368     x = (int) wcslen(buffer);
369     while (!done) {
370         if (x > (int) wcslen(buffer))
371             x = (int) wcslen(buffer);
372
373         /* clear the "window' */
374         wmove(win, y0, x0);
375         wprintw(win, "%*s", limit, " ");
376
377         /* write the existing buffer contents */
378         wmove(win, y0, x0);
379         waddnwstr(win, buffer, limit);
380
381         /* positions the cursor past character 'x' */
382         wmove(win, y0, x0);
383         waddnwstr(win, buffer, x);
384
385         switch (wGet_wchar(win, &ch)) {
386         case KEY_CODE_YES:
387             fkey = TRUE;
388             switch (ch) {
389             case KEY_ENTER:
390                 ch = '\n';
391                 fkey = FALSE;
392                 break;
393             case KEY_BACKSPACE:
394             case KEY_DC:
395                 ch = '\b';
396                 fkey = FALSE;
397                 break;
398             case KEY_LEFT:
399             case KEY_RIGHT:
400                 break;
401             default:
402                 ch = (wint_t) -1;
403                 break;
404             }
405             break;
406         case OK:
407             fkey = FALSE;
408             break;
409         default:
410             ch = (wint_t) -1;
411             fkey = TRUE;
412             break;
413         }
414
415         switch (ch) {
416         case '\n':
417             done = TRUE;
418             break;
419         case CTRL('U'):
420             *buffer = '\0';
421             break;
422         case '\b':
423             if (x > 0) {
424                 int j;
425                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
426                     ;
427                 }
428             } else {
429                 beep();
430             }
431             break;
432         case KEY_LEFT:
433             if (x > 0) {
434                 --x;
435             } else {
436                 beep();
437             }
438             break;
439         case KEY_RIGHT:
440             ++x;
441             break;
442         default:
443             if (fkey) {
444                 beep();
445             } else if ((int) wcslen(buffer) < limit) {
446                 int j;
447                 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
448                     buffer[j] = buffer[j - 1];
449                 }
450                 buffer[x++] = (wchar_t) ch;
451             } else {
452                 beep();
453             }
454         }
455     }
456
457     wattroff(win, A_REVERSE);
458     wmove(win, y0, x0);
459     noecho();
460 }
461
462 #endif
463
464 static void
465 Pause(void)
466 {
467     move(LINES - 1, 0);
468     addstr("Press any key to continue... ");
469     (void) Getchar();
470 }
471
472 static void
473 Cannot(const char *what)
474 {
475     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
476     Pause();
477 }
478
479 static void
480 ShellOut(bool message)
481 {
482     if (message)
483         addstr("Shelling out...");
484     def_prog_mode();
485     endwin();
486     system("sh");
487     if (message)
488         addstr("returned from shellout.\n");
489     refresh();
490 }
491
492 #ifdef NCURSES_MOUSE_VERSION
493 /*
494  * This function is the same as _tracemouse(), but we cannot count on that
495  * being available in the non-debug library.
496  */
497 static const char *
498 mouse_decode(MEVENT const *ep)
499 {
500     static char buf[80 + (5 * 10) + (32 * 15)];
501
502     (void) sprintf(buf, "id %2d  at (%2d, %2d, %2d) state %4lx = {",
503                    ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
504
505 #define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
506
507     SHOW(BUTTON1_RELEASED, "release-1");
508     SHOW(BUTTON1_PRESSED, "press-1");
509     SHOW(BUTTON1_CLICKED, "click-1");
510     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
511     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
512 #if NCURSES_MOUSE_VERSION == 1
513     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
514 #endif
515
516     SHOW(BUTTON2_RELEASED, "release-2");
517     SHOW(BUTTON2_PRESSED, "press-2");
518     SHOW(BUTTON2_CLICKED, "click-2");
519     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
520     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
521 #if NCURSES_MOUSE_VERSION == 1
522     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
523 #endif
524
525     SHOW(BUTTON3_RELEASED, "release-3");
526     SHOW(BUTTON3_PRESSED, "press-3");
527     SHOW(BUTTON3_CLICKED, "click-3");
528     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
529     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
530 #if NCURSES_MOUSE_VERSION == 1
531     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
532 #endif
533
534     SHOW(BUTTON4_RELEASED, "release-4");
535     SHOW(BUTTON4_PRESSED, "press-4");
536     SHOW(BUTTON4_CLICKED, "click-4");
537     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
538     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
539 #if NCURSES_MOUSE_VERSION == 1
540     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
541 #endif
542
543 #if NCURSES_MOUSE_VERSION == 2
544     SHOW(BUTTON5_RELEASED, "release-5");
545     SHOW(BUTTON5_PRESSED, "press-5");
546     SHOW(BUTTON5_CLICKED, "click-5");
547     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
548     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
549 #endif
550
551     SHOW(BUTTON_CTRL, "ctrl");
552     SHOW(BUTTON_SHIFT, "shift");
553     SHOW(BUTTON_ALT, "alt");
554     SHOW(ALL_MOUSE_EVENTS, "all-events");
555     SHOW(REPORT_MOUSE_POSITION, "position");
556
557 #undef SHOW
558
559     if (buf[strlen(buf) - 1] == ' ')
560         buf[strlen(buf) - 2] = '\0';
561     (void) strcat(buf, "}");
562     return (buf);
563 }
564 #endif /* NCURSES_MOUSE_VERSION */
565
566 /****************************************************************************
567  *
568  * Character input test
569  *
570  ****************************************************************************/
571
572 #define NUM_GETCH_FLAGS 256
573 typedef bool GetchFlags[NUM_GETCH_FLAGS];
574
575 static void
576 setup_getch(WINDOW *win, GetchFlags flags)
577 {
578     keypad(win, flags['k']);    /* should be redundant, but for testing */
579     meta(win, flags['m']);      /* force this to a known state */
580     if (flags['e'])
581         echo();
582     else
583         noecho();
584 }
585
586 static void
587 init_getch(WINDOW *win, GetchFlags flags)
588 {
589     memset(flags, FALSE, NUM_GETCH_FLAGS);
590     flags[UChar('k')] = (win == stdscr);
591     flags[UChar('m')] = TRUE;
592
593     setup_getch(win, flags);
594 }
595
596 static void
597 wgetch_help(WINDOW *win, GetchFlags flags)
598 {
599     static const char *help[] =
600     {
601         "e  -- toggle echo mode"
602         ,"g  -- triggers a getstr test"
603         ,"k  -- toggle keypad/literal mode"
604         ,"m  -- toggle meta (7-bit/8-bit) mode"
605         ,"^q -- quit"
606         ,"s  -- shell out\n"
607         ,"w  -- create a new window"
608 #ifdef SIGTSTP
609         ,"z  -- suspend this process"
610 #endif
611     };
612     int y, x;
613     unsigned chk = ((SIZEOF(help) + 1) / 2);
614     unsigned n;
615
616     getyx(win, y, x);
617     move(0, 0);
618     printw("Type any key to see its %s value.  Also:\n",
619            flags['k'] ? "keypad" : "literal");
620     for (n = 0; n < SIZEOF(help); ++n) {
621         int row = 1 + (int) (n % chk);
622         int col = (n >= chk) ? COLS / 2 : 0;
623         int flg = ((strstr(help[n], "toggle") != 0)
624                    && (flags[UChar(*help[n])] != FALSE));
625         if (flg)
626             standout();
627         mvprintw(row, col, "%s", help[n]);
628         if (col == 0)
629             clrtoeol();
630         if (flg)
631             standend();
632     }
633     wrefresh(stdscr);
634     wmove(win, y, x);
635 }
636
637 static void
638 wgetch_wrap(WINDOW *win, int first_y)
639 {
640     int last_y = getmaxy(win) - 1;
641     int y = getcury(win) + 1;
642
643     if (y >= last_y)
644         y = first_y;
645     wmove(win, y, 0);
646     wclrtoeol(win);
647 }
648
649 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
650 typedef struct {
651     WINDOW *text;
652     WINDOW *frame;
653 } WINSTACK;
654
655 static WINSTACK *winstack = 0;
656 static unsigned len_winstack = 0;
657
658 static void
659 forget_boxes(void)
660 {
661     if (winstack != 0) {
662         free(winstack);
663     }
664     winstack = 0;
665     len_winstack = 0;
666 }
667
668 static void
669 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
670 {
671     unsigned need = (level + 1) * 2;
672
673     assert(level < COLS);
674
675     if (winstack == 0) {
676         len_winstack = 20;
677         winstack = typeMalloc(WINSTACK, len_winstack);
678     } else if (need >= len_winstack) {
679         len_winstack = need;
680         winstack = typeRealloc(WINSTACK, len_winstack, winstack);
681     }
682     winstack[level].text = txt_win;
683     winstack[level].frame = box_win;
684 }
685
686 #if USE_SOFTKEYS && (NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
687 static void
688 slk_repaint(void)
689 {
690     /* this chunk is now done in resize_term() */
691     slk_touch();
692     slk_clear();
693     slk_noutrefresh();
694 }
695
696 #else
697 #define slk_repaint()           /* nothing */
698 #endif
699
700 /*
701  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
702  * Resize both and paint the box in the parent.
703  */
704 static void
705 resize_boxes(unsigned level, WINDOW *win)
706 {
707     unsigned n;
708     int base = 5;
709     int high = LINES - base;
710     int wide = COLS;
711
712     touchwin(stdscr);
713     wnoutrefresh(stdscr);
714
715     slk_repaint();
716
717     for (n = 0; n < level; ++n) {
718         wresize(winstack[n].frame, high, wide);
719         wresize(winstack[n].text, high - 2, wide - 2);
720         high -= 2;
721         wide -= 2;
722         werase(winstack[n].text);
723         box(winstack[n].frame, 0, 0);
724         wnoutrefresh(winstack[n].frame);
725         wprintw(winstack[n].text,
726                 "size %dx%d\n",
727                 getmaxy(winstack[n].text),
728                 getmaxx(winstack[n].text));
729         wnoutrefresh(winstack[n].text);
730         if (winstack[n].text == win)
731             break;
732     }
733     doupdate();
734 }
735 #else
736 #define forget_boxes()          /* nothing */
737 #define remember_boxes(level,text,frame)        /* nothing */
738 #endif
739
740 static void
741 wgetch_test(unsigned level, WINDOW *win, int delay)
742 {
743     char buf[BUFSIZ];
744     int first_y, first_x;
745     int c;
746     int incount = 0;
747     GetchFlags flags;
748     bool blocking = (delay < 0);
749
750     init_getch(win, flags);
751     wtimeout(win, delay);
752     getyx(win, first_y, first_x);
753
754     wgetch_help(win, flags);
755     wsetscrreg(win, first_y, getmaxy(win) - 1);
756     scrollok(win, TRUE);
757
758     for (;;) {
759         while ((c = wGetchar(win)) == ERR) {
760             incount++;
761             if (blocking) {
762                 (void) wprintw(win, "%05d: input error", incount);
763                 break;
764             } else {
765                 (void) wprintw(win, "%05d: input timed out", incount);
766             }
767             wgetch_wrap(win, first_y);
768         }
769         if (c == ERR && blocking) {
770             wprintw(win, "ERR");
771             wgetch_wrap(win, first_y);
772         } else if (isQuit(c)) {
773             break;
774         } else if (c == 'e') {
775             flags[UChar('e')] = !flags[UChar('e')];
776             setup_getch(win, flags);
777             wgetch_help(win, flags);
778         } else if (c == 'g') {
779             waddstr(win, "getstr test: ");
780             echo();
781             wgetnstr(win, buf, sizeof(buf) - 1);
782             noecho();
783             wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
784             wclrtoeol(win);
785             wgetch_wrap(win, first_y);
786         } else if (c == 'k') {
787             flags[UChar('k')] = !flags[UChar('k')];
788             setup_getch(win, flags);
789             wgetch_help(win, flags);
790         } else if (c == 'm') {
791             flags[UChar('m')] = !flags[UChar('m')];
792             setup_getch(win, flags);
793             wgetch_help(win, flags);
794         } else if (c == 's') {
795             ShellOut(TRUE);
796         } else if (c == 'w') {
797             int high = getmaxy(win) - 1 - first_y + 1;
798             int wide = getmaxx(win) - first_x;
799             int old_y, old_x;
800             int new_y = first_y + getbegy(win);
801             int new_x = first_x + getbegx(win);
802
803             getyx(win, old_y, old_x);
804             if (high > 2 && wide > 2) {
805                 WINDOW *wb = newwin(high, wide, new_y, new_x);
806                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
807
808                 box(wb, 0, 0);
809                 wrefresh(wb);
810                 wmove(wi, 0, 0);
811                 remember_boxes(level, wi, wb);
812                 wgetch_test(level + 1, wi, delay);
813                 delwin(wi);
814                 delwin(wb);
815
816                 wgetch_help(win, flags);
817                 wmove(win, old_y, old_x);
818                 touchwin(win);
819                 wrefresh(win);
820                 doupdate();
821             }
822 #ifdef SIGTSTP
823         } else if (c == 'z') {
824             kill(getpid(), SIGTSTP);
825 #endif
826         } else {
827             wprintw(win, "Key pressed: %04o ", c);
828 #ifdef NCURSES_MOUSE_VERSION
829             if (c == KEY_MOUSE) {
830                 int y, x;
831                 MEVENT event;
832
833                 getmouse(&event);
834                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
835                 getyx(win, y, x);
836                 move(event.y, event.x);
837                 addch('*');
838                 wmove(win, y, x);
839             } else
840 #endif /* NCURSES_MOUSE_VERSION */
841             if (c >= KEY_MIN) {
842 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
843                 if (c == KEY_RESIZE) {
844                     resize_boxes(level, win);
845                 }
846 #endif
847                 (void) waddstr(win, keyname(c));
848             } else if (c >= 0x80) {
849                 unsigned c2 = (unsigned) c;
850 #if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
851                 /* at least Solaris SVR4 curses breaks unctrl(128), etc. */
852                 c2 &= 0x7f;
853 #endif
854                 if (isprint(c))
855                     (void) wprintw(win, "%c", UChar(c));
856                 else if (c2 != UChar(c))
857                     (void) wprintw(win, "M-%s", unctrl(c2));
858                 else
859                     (void) wprintw(win, "%s", unctrl(c2));
860                 waddstr(win, " (high-half character)");
861             } else {
862                 if (isprint(c))
863                     (void) wprintw(win, "%c (ASCII printable character)", c);
864                 else
865                     (void) wprintw(win, "%s (ASCII control character)",
866                                    unctrl(UChar(c)));
867             }
868             wgetch_wrap(win, first_y);
869         }
870     }
871
872     wtimeout(win, -1);
873
874     if (!level)
875         init_getch(win, flags);
876 }
877
878 static int
879 begin_getch_test(void)
880 {
881     char buf[BUFSIZ];
882     int delay;
883
884     refresh();
885
886 #ifdef NCURSES_MOUSE_VERSION
887     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
888 #endif
889
890     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
891     echo();
892     getnstr(buf, sizeof(buf) - 1);
893     noecho();
894     nonl();
895
896     if (isdigit(UChar(buf[0]))) {
897         delay = atoi(buf) * 100;
898     } else {
899         delay = -1;
900     }
901     raw();
902     move(5, 0);
903     return delay;
904 }
905
906 static void
907 finish_getch_test(void)
908 {
909 #ifdef NCURSES_MOUSE_VERSION
910     mousemask(0, (mmask_t *) 0);
911 #endif
912     erase();
913     noraw();
914     nl();
915     endwin();
916 }
917
918 static void
919 getch_test(void)
920 {
921     int delay = begin_getch_test();
922
923     slk_restore();
924     wgetch_test(0, stdscr, delay);
925     forget_boxes();
926     finish_getch_test();
927 }
928
929 #if USE_WIDEC_SUPPORT
930 /*
931  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
932  * Resize both and paint the box in the parent.
933  */
934 #if defined(KEY_RESIZE) && HAVE_WRESIZE
935 static void
936 resize_wide_boxes(unsigned level, WINDOW *win)
937 {
938     unsigned n;
939     int base = 5;
940     int high = LINES - base;
941     int wide = COLS;
942
943     touchwin(stdscr);
944     wnoutrefresh(stdscr);
945
946     slk_repaint();
947
948     for (n = 0; n < level; ++n) {
949         wresize(winstack[n].frame, high, wide);
950         wresize(winstack[n].text, high - 2, wide - 2);
951         high -= 2;
952         wide -= 2;
953         werase(winstack[n].text);
954         box_set(winstack[n].frame, 0, 0);
955         wnoutrefresh(winstack[n].frame);
956         wprintw(winstack[n].text,
957                 "size %dx%d\n",
958                 getmaxy(winstack[n].text),
959                 getmaxx(winstack[n].text));
960         wnoutrefresh(winstack[n].text);
961         if (winstack[n].text == win)
962             break;
963     }
964     doupdate();
965 }
966 #endif /* KEY_RESIZE */
967
968 static char *
969 wcstos(const wchar_t *src)
970 {
971     int need;
972     char *result = 0;
973     const wchar_t *tmp = src;
974 #ifndef state_unused
975     mbstate_t state;
976 #endif
977
978     reset_wchars(state);
979     if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
980         unsigned have = (unsigned) need;
981         if ((result = typeCalloc(char, have + 1)) != 0) {
982             tmp = src;
983             if (trans_wchars(result, tmp, have, &state) != have) {
984                 free(result);
985                 result = 0;
986             }
987         }
988     }
989     return result;
990 }
991
992 static void
993 wget_wch_test(unsigned level, WINDOW *win, int delay)
994 {
995     wchar_t wchar_buf[BUFSIZ];
996     wint_t wint_buf[BUFSIZ];
997     int first_y, first_x;
998     wint_t c;
999     int incount = 0;
1000     GetchFlags flags;
1001     bool blocking = (delay < 0);
1002     int y, x, code;
1003     char *temp;
1004
1005     init_getch(win, flags);
1006     wtimeout(win, delay);
1007     getyx(win, first_y, first_x);
1008
1009     wgetch_help(win, flags);
1010     wsetscrreg(win, first_y, getmaxy(win) - 1);
1011     scrollok(win, TRUE);
1012
1013     for (;;) {
1014         while ((code = wGet_wchar(win, &c)) == ERR) {
1015             incount++;
1016             if (blocking) {
1017                 (void) wprintw(win, "%05d: input error", incount);
1018                 break;
1019             } else {
1020                 (void) wprintw(win, "%05d: input timed out", incount);
1021             }
1022             wgetch_wrap(win, first_y);
1023         }
1024         if (code == ERR && blocking) {
1025             wprintw(win, "ERR");
1026             wgetch_wrap(win, first_y);
1027         } else if (isQuit((int) c)) {
1028             break;
1029         } else if (c == 'e') {
1030             flags[UChar('e')] = !flags[UChar('e')];
1031             setup_getch(win, flags);
1032             wgetch_help(win, flags);
1033         } else if (c == 'g') {
1034             waddstr(win, "getstr test: ");
1035             echo();
1036             code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
1037             noecho();
1038             if (code == ERR) {
1039                 wprintw(win, "wgetn_wstr returns an error.");
1040             } else {
1041                 int n;
1042                 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1043                     ;
1044                 }
1045                 if ((temp = wcstos(wchar_buf)) != 0) {
1046                     wprintw(win, "I saw %d characters:\n\t`%s'.",
1047                             (int) wcslen(wchar_buf), temp);
1048                     free(temp);
1049                 } else {
1050                     wprintw(win, "I saw %d characters (cannot convert).",
1051                             (int) wcslen(wchar_buf));
1052                 }
1053             }
1054             wclrtoeol(win);
1055             wgetch_wrap(win, first_y);
1056         } else if (c == 'k') {
1057             flags[UChar('k')] = !flags[UChar('k')];
1058             setup_getch(win, flags);
1059             wgetch_help(win, flags);
1060         } else if (c == 'm') {
1061             flags[UChar('m')] = !flags[UChar('m')];
1062             setup_getch(win, flags);
1063             wgetch_help(win, flags);
1064         } else if (c == 's') {
1065             ShellOut(TRUE);
1066         } else if (c == 'w') {
1067             int high = getmaxy(win) - 1 - first_y + 1;
1068             int wide = getmaxx(win) - first_x;
1069             int old_y, old_x;
1070             int new_y = first_y + getbegy(win);
1071             int new_x = first_x + getbegx(win);
1072
1073             getyx(win, old_y, old_x);
1074             if (high > 2 && wide > 2) {
1075                 WINDOW *wb = newwin(high, wide, new_y, new_x);
1076                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1077
1078                 box_set(wb, 0, 0);
1079                 wrefresh(wb);
1080                 wmove(wi, 0, 0);
1081                 remember_boxes(level, wi, wb);
1082                 wget_wch_test(level + 1, wi, delay);
1083                 delwin(wi);
1084                 delwin(wb);
1085
1086                 wgetch_help(win, flags);
1087                 wmove(win, old_y, old_x);
1088                 touchwin(win);
1089                 wrefresh(win);
1090             }
1091 #ifdef SIGTSTP
1092         } else if (c == 'z') {
1093             kill(getpid(), SIGTSTP);
1094 #endif
1095         } else {
1096             wprintw(win, "Key pressed: %04o ", (int) c);
1097 #ifdef NCURSES_MOUSE_VERSION
1098             if (c == KEY_MOUSE) {
1099                 MEVENT event;
1100
1101                 getmouse(&event);
1102                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
1103                 getyx(win, y, x);
1104                 move(event.y, event.x);
1105                 addch('*');
1106                 wmove(win, y, x);
1107             } else
1108 #endif /* NCURSES_MOUSE_VERSION */
1109             if (code == KEY_CODE_YES) {
1110 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1111                 if (c == KEY_RESIZE) {
1112                     resize_wide_boxes(level, win);
1113                 }
1114 #endif
1115                 (void) waddstr(win, keyname((wchar_t) c));
1116             } else {
1117                 (void) waddstr(win, key_name((wchar_t) c));
1118                 if (c < 256 && iscntrl(c)) {
1119                     (void) wprintw(win, " (control character)");
1120                 } else {
1121                     (void) wprintw(win, " = %#x (printable character)",
1122                                    (unsigned) c);
1123                 }
1124             }
1125             wgetch_wrap(win, first_y);
1126         }
1127     }
1128
1129     wtimeout(win, -1);
1130
1131     if (!level)
1132         init_getch(win, flags);
1133 }
1134
1135 static void
1136 get_wch_test(void)
1137 {
1138     int delay = begin_getch_test();
1139
1140     slk_restore();
1141     wget_wch_test(0, stdscr, delay);
1142     forget_boxes();
1143     finish_getch_test();
1144 }
1145 #endif
1146
1147 /****************************************************************************
1148  *
1149  * Character attributes test
1150  *
1151  ****************************************************************************/
1152
1153 #if HAVE_SETUPTERM || HAVE_TGETENT
1154 #define get_ncv() TIGETNUM("ncv","NC")
1155 #define get_xmc() TIGETNUM("xmc","sg")
1156 #else
1157 #define get_ncv() -1
1158 #define get_xmc() -1
1159 #endif
1160
1161 #if !HAVE_TERMATTRS
1162 static chtype
1163 my_termattrs(void)
1164 {
1165     static int first = TRUE;
1166     static chtype result = 0;
1167
1168     if (first) {
1169 #if !HAVE_TIGETSTR
1170         char buffer[4096];
1171         char parsed[4096];
1172         char *area_pointer = parsed;
1173
1174         tgetent(buffer, getenv("TERM"));
1175 #endif
1176
1177         if (TIGETSTR("smso", "so"))
1178             result |= A_STANDOUT;
1179         if (TIGETSTR("smul", "us"))
1180             result |= A_UNDERLINE;
1181         if (TIGETSTR("rev", "mr"))
1182             result |= A_REVERSE;
1183         if (TIGETSTR("blink", "mb"))
1184             result |= A_BLINK;
1185         if (TIGETSTR("dim", "mh"))
1186             result |= A_DIM;
1187         if (TIGETSTR("bold", "md"))
1188             result |= A_BOLD;
1189         if (TIGETSTR("smacs", "ac"))
1190             result |= A_ALTCHARSET;
1191
1192         first = FALSE;
1193     }
1194     return result;
1195 }
1196 #define termattrs() my_termattrs()
1197 #endif
1198
1199 #define MAX_ATTRSTRING 31
1200 #define LEN_ATTRSTRING 26
1201
1202 static char attr_test_string[MAX_ATTRSTRING + 1];
1203
1204 static void
1205 attr_legend(WINDOW *helpwin)
1206 {
1207     int row = 1;
1208     int col = 1;
1209
1210     mvwprintw(helpwin, row++, col,
1211               "ESC to exit.");
1212     mvwprintw(helpwin, row++, col,
1213               "^L repaints.");
1214     ++row;
1215     mvwprintw(helpwin, row++, col,
1216               "Modify the test strings:");
1217     mvwprintw(helpwin, row++, col,
1218               "  A digit sets gaps on each side of displayed attributes");
1219     mvwprintw(helpwin, row++, col,
1220               "  </> shifts the text left/right. ");
1221     ++row;
1222     mvwprintw(helpwin, row++, col,
1223               "Toggles:");
1224     if (use_colors) {
1225         mvwprintw(helpwin, row++, col,
1226                   "  f/F/b/F toggle foreground/background background color");
1227         mvwprintw(helpwin, row++, col,
1228                   "  t/T     toggle text/background color attribute");
1229     }
1230     mvwprintw(helpwin, row++, col,
1231               "  a/A     toggle ACS (alternate character set) mapping");
1232     mvwprintw(helpwin, row++, col,
1233               "  v/V     toggle video attribute to combine with each line");
1234 }
1235
1236 static void
1237 show_color_attr(int fg, int bg, int tx)
1238 {
1239     if (use_colors) {
1240         printw("  Colors (fg %d, bg %d", fg, bg);
1241         if (tx >= 0)
1242             printw(", text %d", tx);
1243         printw("),");
1244     }
1245 }
1246
1247 static bool
1248 cycle_color_attr(int ch, short *fg, short *bg, short *tx)
1249 {
1250     bool error = FALSE;
1251
1252     if (use_colors) {
1253         switch (ch) {
1254         case 'f':
1255             *fg = (short) (*fg + 1);
1256             break;
1257         case 'F':
1258             *fg = (short) (*fg - 1);
1259             break;
1260         case 'b':
1261             *bg = (short) (*bg + 1);
1262             break;
1263         case 'B':
1264             *bg = (short) (*bg - 1);
1265             break;
1266         case 't':
1267             *tx = (short) (*tx + 1);
1268             break;
1269         case 'T':
1270             *tx = (short) (*tx - 1);
1271             break;
1272         default:
1273             beep();
1274             error = TRUE;
1275             break;
1276         }
1277         if (*fg >= COLORS)
1278             *fg = (short) min_colors;
1279         if (*fg < min_colors)
1280             *fg = (short) (COLORS - 1);
1281         if (*bg >= COLORS)
1282             *bg = (short) min_colors;
1283         if (*bg < min_colors)
1284             *bg = (short) (COLORS - 1);
1285         if (*tx >= COLORS)
1286             *tx = -1;
1287         if (*tx < -1)
1288             *tx = (short) (COLORS - 1);
1289     } else {
1290         beep();
1291         error = TRUE;
1292     }
1293     return error;
1294 }
1295
1296 static void
1297 adjust_attr_string(int adjust)
1298 {
1299     int first = ((int) UChar(attr_test_string[0])) + adjust;
1300     int last = first + LEN_ATTRSTRING;
1301
1302     if (first >= ' ' && last <= '~') {  /* 32..126 */
1303         int j, k;
1304         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1305             attr_test_string[j] = (char) k;
1306             if (((k + 1 - first) % 5) == 0) {
1307                 if (++j >= MAX_ATTRSTRING)
1308                     break;
1309                 attr_test_string[j] = ' ';
1310             }
1311         }
1312         while (j < MAX_ATTRSTRING)
1313             attr_test_string[j++] = ' ';
1314         attr_test_string[j] = '\0';
1315     } else {
1316         beep();
1317     }
1318 }
1319
1320 static void
1321 init_attr_string(void)
1322 {
1323     attr_test_string[0] = 'a';
1324     adjust_attr_string(0);
1325 }
1326
1327 static int
1328 show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
1329 {
1330     int ncv = get_ncv();
1331     chtype test = attr & (chtype) (~A_ALTCHARSET);
1332
1333     if (arrow)
1334         mvprintw(row, 5, "-->");
1335     mvprintw(row, 8, "%s mode:", name);
1336     mvprintw(row, 24, "|");
1337     if (skip)
1338         printw("%*s", skip, " ");
1339     /*
1340      * Just for testing, write text using the alternate character set one
1341      * character at a time (to pass its rendition directly), and use the
1342      * string operation for the other attributes.
1343      */
1344     if (attr & A_ALTCHARSET) {
1345         const char *s;
1346         chtype ch;
1347
1348         for (s = attr_test_string; *s != '\0'; ++s) {
1349             ch = UChar(*s);
1350             addch(ch | attr);
1351         }
1352     } else {
1353         attrset(attr);
1354         addstr(attr_test_string);
1355         attroff(attr);
1356     }
1357     if (skip)
1358         printw("%*s", skip, " ");
1359     printw("|");
1360     if (test != A_NORMAL) {
1361         if (!(termattrs() & test)) {
1362             printw(" (N/A)");
1363         } else {
1364             if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
1365                 static const chtype table[] =
1366                 {
1367                     A_STANDOUT,
1368                     A_UNDERLINE,
1369                     A_REVERSE,
1370                     A_BLINK,
1371                     A_DIM,
1372                     A_BOLD,
1373 #ifdef A_INVIS
1374                     A_INVIS,
1375 #endif
1376                     A_PROTECT,
1377                     A_ALTCHARSET
1378                 };
1379                 unsigned n;
1380                 bool found = FALSE;
1381                 for (n = 0; n < SIZEOF(table); n++) {
1382                     if ((table[n] & attr) != 0
1383                         && ((1 << n) & ncv) != 0) {
1384                         found = TRUE;
1385                         break;
1386                     }
1387                 }
1388                 if (found)
1389                     printw(" (NCV)");
1390             }
1391             if ((termattrs() & test) != test)
1392                 printw(" (Part)");
1393         }
1394     }
1395     return row + 2;
1396 }
1397 /* *INDENT-OFF* */
1398 static const struct {
1399     chtype                      attr;
1400     NCURSES_CONST char *        name;
1401 } attrs_to_test[] = {
1402     { A_STANDOUT,       "STANDOUT" },
1403     { A_REVERSE,        "REVERSE" },
1404     { A_BOLD,           "BOLD" },
1405     { A_UNDERLINE,      "UNDERLINE" },
1406     { A_DIM,            "DIM" },
1407     { A_BLINK,          "BLINK" },
1408     { A_PROTECT,        "PROTECT" },
1409 #ifdef A_INVIS
1410     { A_INVIS,          "INVISIBLE" },
1411 #endif
1412     { A_NORMAL,         "NORMAL" },
1413 };
1414 /* *INDENT-ON* */
1415
1416 static bool
1417 attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1418 {
1419     bool result = TRUE;
1420     bool error = FALSE;
1421     WINDOW *helpwin;
1422
1423     do {
1424         int ch = Getchar();
1425
1426         error = FALSE;
1427         if (ch < 256 && isdigit(ch)) {
1428             *skip = (ch - '0');
1429         } else {
1430             switch (ch) {
1431             case CTRL('L'):
1432                 Repaint();
1433                 break;
1434             case '?':
1435                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1436                     box(helpwin, 0, 0);
1437                     attr_legend(helpwin);
1438                     wGetchar(helpwin);
1439                     delwin(helpwin);
1440                 }
1441                 break;
1442             case 'a':
1443                 *ac = 0;
1444                 break;
1445             case 'A':
1446                 *ac = A_ALTCHARSET;
1447                 break;
1448             case 'v':
1449                 if (*kc == 0)
1450                     *kc = SIZEOF(attrs_to_test) - 1;
1451                 else
1452                     *kc -= 1;
1453                 break;
1454             case 'V':
1455                 *kc += 1;
1456                 if (*kc >= SIZEOF(attrs_to_test))
1457                     *kc = 0;
1458                 break;
1459             case '<':
1460                 adjust_attr_string(-1);
1461                 break;
1462             case '>':
1463                 adjust_attr_string(1);
1464                 break;
1465             case case_QUIT:
1466                 result = FALSE;
1467                 break;
1468             default:
1469                 error = cycle_color_attr(ch, fg, bg, tx);
1470                 break;
1471             }
1472         }
1473     } while (error);
1474     return result;
1475 }
1476
1477 static void
1478 attr_test(void)
1479 /* test text attributes */
1480 {
1481     int n;
1482     int skip = get_xmc();
1483     short fg = COLOR_BLACK;     /* color pair 0 is special */
1484     short bg = COLOR_BLACK;
1485     short tx = -1;
1486     int ac = 0;
1487     unsigned j, k;
1488
1489     if (skip < 0)
1490         skip = 0;
1491
1492     n = skip;                   /* make it easy */
1493     k = SIZEOF(attrs_to_test) - 1;
1494     init_attr_string();
1495
1496     do {
1497         int row = 2;
1498         chtype normal = A_NORMAL | BLANK;
1499         chtype extras = (chtype) ac;
1500
1501         if (use_colors) {
1502             short pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1503             if (pair != 0) {
1504                 pair = 1;
1505                 if (init_pair(pair, fg, bg) == ERR) {
1506                     beep();
1507                 } else {
1508                     normal |= COLOR_PAIR(pair);
1509                 }
1510             }
1511             if (tx >= 0) {
1512                 pair = 2;
1513                 if (init_pair(pair, tx, bg) == ERR) {
1514                     beep();
1515                 } else {
1516                     extras |= COLOR_PAIR(pair);
1517                 }
1518             }
1519         }
1520         bkgd(normal);
1521         bkgdset(normal);
1522         erase();
1523
1524         box(stdscr, 0, 0);
1525         mvaddstr(0, 20, "Character attribute test display");
1526
1527         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1528             bool arrow = (j == k);
1529             row = show_attr(row, n, arrow,
1530                             extras |
1531                             attrs_to_test[j].attr |
1532                             attrs_to_test[k].attr,
1533                             attrs_to_test[j].name);
1534         }
1535
1536         mvprintw(row, 8,
1537                  "This terminal does %shave the magic-cookie glitch",
1538                  get_xmc() > -1 ? "" : "not ");
1539         mvprintw(row + 1, 8, "Enter '?' for help.");
1540         show_color_attr(fg, bg, tx);
1541         printw("  ACS (%d)", ac != 0);
1542
1543         refresh();
1544     } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1545
1546     bkgdset(A_NORMAL | BLANK);
1547     erase();
1548     endwin();
1549 }
1550
1551 #if USE_WIDEC_SUPPORT
1552 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1553
1554 static void
1555 wide_adjust_attr_string(int adjust)
1556 {
1557     int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
1558     int last = first + LEN_ATTRSTRING;
1559
1560     if (first >= ' ' && last <= '~') {  /* 32..126 */
1561         int j, k;
1562         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1563             wide_attr_test_string[j] = k;
1564             if (((k + 1 - first) % 5) == 0) {
1565                 if (++j >= MAX_ATTRSTRING)
1566                     break;
1567                 wide_attr_test_string[j] = ' ';
1568             }
1569         }
1570         while (j < MAX_ATTRSTRING)
1571             wide_attr_test_string[j++] = ' ';
1572         wide_attr_test_string[j] = '\0';
1573     } else {
1574         beep();
1575     }
1576 }
1577
1578 static void
1579 wide_init_attr_string(void)
1580 {
1581     wide_attr_test_string[0] = 'a';
1582     wide_adjust_attr_string(0);
1583 }
1584
1585 static void
1586 set_wide_background(short pair)
1587 {
1588     cchar_t normal;
1589     wchar_t blank[2];
1590
1591     blank[0] = ' ';
1592     blank[1] = 0;
1593     setcchar(&normal, blank, A_NORMAL, pair, 0);
1594     bkgrnd(&normal);
1595     bkgrndset(&normal);
1596 }
1597
1598 static attr_t
1599 get_wide_background(void)
1600 {
1601     attr_t result = A_NORMAL;
1602     attr_t attr;
1603     cchar_t ch;
1604     short pair;
1605     wchar_t wch[10];
1606
1607     if (getbkgrnd(&ch) != ERR) {
1608         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1609             result = attr;
1610         }
1611     }
1612     return result;
1613 }
1614
1615 static int
1616 wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
1617 {
1618     int ncv = get_ncv();
1619     chtype test = attr & ~WA_ALTCHARSET;
1620
1621     if (arrow)
1622         mvprintw(row, 5, "-->");
1623     mvprintw(row, 8, "%s mode:", name);
1624     mvprintw(row, 24, "|");
1625     if (skip)
1626         printw("%*s", skip, " ");
1627
1628     /*
1629      * Just for testing, write text using the alternate character set one
1630      * character at a time (to pass its rendition directly), and use the
1631      * string operation for the other attributes.
1632      */
1633     if (attr & WA_ALTCHARSET) {
1634         const wchar_t *s;
1635         cchar_t ch;
1636
1637         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1638             wchar_t fill[2];
1639             fill[0] = *s;
1640             fill[1] = L'\0';
1641             setcchar(&ch, fill, attr, pair, 0);
1642             add_wch(&ch);
1643         }
1644     } else {
1645         attr_t old_attr;
1646         short old_pair;
1647
1648         attr_get(&old_attr, &old_pair, 0);
1649         attr_set(attr, pair, 0);
1650         addwstr(wide_attr_test_string);
1651         attr_set(old_attr, old_pair, 0);
1652     }
1653     if (skip)
1654         printw("%*s", skip, " ");
1655     printw("|");
1656     if (test != A_NORMAL) {
1657         if (!(term_attrs() & test)) {
1658             printw(" (N/A)");
1659         } else {
1660             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1661                 static const attr_t table[] =
1662                 {
1663                     WA_STANDOUT,
1664                     WA_UNDERLINE,
1665                     WA_REVERSE,
1666                     WA_BLINK,
1667                     WA_DIM,
1668                     WA_BOLD,
1669                     WA_INVIS,
1670                     WA_PROTECT,
1671                     WA_ALTCHARSET
1672                 };
1673                 unsigned n;
1674                 bool found = FALSE;
1675                 for (n = 0; n < SIZEOF(table); n++) {
1676                     if ((table[n] & attr) != 0
1677                         && ((1 << n) & ncv) != 0) {
1678                         found = TRUE;
1679                         break;
1680                     }
1681                 }
1682                 if (found)
1683                     printw(" (NCV)");
1684             }
1685             if ((term_attrs() & test) != test)
1686                 printw(" (Part)");
1687         }
1688     }
1689     return row + 2;
1690 }
1691
1692 static bool
1693 wide_attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1694 {
1695     bool result = TRUE;
1696     bool error = FALSE;
1697     WINDOW *helpwin;
1698
1699     do {
1700         int ch = Getchar();
1701
1702         error = FALSE;
1703         if (ch < 256 && isdigit(ch)) {
1704             *skip = (ch - '0');
1705         } else {
1706             switch (ch) {
1707             case CTRL('L'):
1708                 Repaint();
1709                 break;
1710             case '?':
1711                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1712                     box_set(helpwin, 0, 0);
1713                     attr_legend(helpwin);
1714                     wGetchar(helpwin);
1715                     delwin(helpwin);
1716                 }
1717                 break;
1718             case 'a':
1719                 *ac = 0;
1720                 break;
1721             case 'A':
1722                 *ac = A_ALTCHARSET;
1723                 break;
1724             case 'v':
1725                 if (*kc == 0)
1726                     *kc = SIZEOF(attrs_to_test) - 1;
1727                 else
1728                     *kc -= 1;
1729                 break;
1730             case 'V':
1731                 *kc += 1;
1732                 if (*kc >= SIZEOF(attrs_to_test))
1733                     *kc = 0;
1734                 break;
1735             case '<':
1736                 wide_adjust_attr_string(-1);
1737                 break;
1738             case '>':
1739                 wide_adjust_attr_string(1);
1740                 break;
1741             case case_QUIT:
1742                 result = FALSE;
1743                 break;
1744             default:
1745                 error = cycle_color_attr(ch, fg, bg, tx);
1746                 break;
1747             }
1748         }
1749     } while (error);
1750     return result;
1751 }
1752
1753 static void
1754 wide_attr_test(void)
1755 /* test text attributes using wide-character calls */
1756 {
1757     int n;
1758     int skip = get_xmc();
1759     short fg = COLOR_BLACK;     /* color pair 0 is special */
1760     short bg = COLOR_BLACK;
1761     short tx = -1;
1762     int ac = 0;
1763     unsigned j, k;
1764
1765     if (skip < 0)
1766         skip = 0;
1767
1768     n = skip;                   /* make it easy */
1769     k = SIZEOF(attrs_to_test) - 1;
1770     wide_init_attr_string();
1771
1772     do {
1773         int row = 2;
1774         short pair = 0;
1775         short extras = 0;
1776
1777         if (use_colors) {
1778             pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1779             if (pair != 0) {
1780                 pair = 1;
1781                 if (init_pair(pair, fg, bg) == ERR) {
1782                     beep();
1783                 }
1784             }
1785             extras = pair;
1786             if (tx >= 0) {
1787                 extras = 2;
1788                 if (init_pair(extras, tx, bg) == ERR) {
1789                     beep();
1790                 }
1791             }
1792         }
1793         set_wide_background(pair);
1794         erase();
1795
1796         box_set(stdscr, 0, 0);
1797         mvaddstr(0, 20, "Character attribute test display");
1798
1799         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1800             row = wide_show_attr(row, n, j == k,
1801                                  ac |
1802                                  attrs_to_test[j].attr |
1803                                  attrs_to_test[k].attr,
1804                                  extras,
1805                                  attrs_to_test[j].name);
1806         }
1807
1808         mvprintw(row, 8,
1809                  "This terminal does %shave the magic-cookie glitch",
1810                  get_xmc() > -1 ? "" : "not ");
1811         mvprintw(row + 1, 8, "Enter '?' for help.");
1812         show_color_attr(fg, bg, tx);
1813         printw("  ACS (%d)", ac != 0);
1814
1815         refresh();
1816     } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1817
1818     set_wide_background(0);
1819     erase();
1820     endwin();
1821 }
1822 #endif
1823
1824 /****************************************************************************
1825  *
1826  * Color support tests
1827  *
1828  ****************************************************************************/
1829
1830 static NCURSES_CONST char *the_color_names[] =
1831 {
1832     "black",
1833     "red",
1834     "green",
1835     "yellow",
1836     "blue",
1837     "magenta",
1838     "cyan",
1839     "white",
1840     "BLACK",
1841     "RED",
1842     "GREEN",
1843     "YELLOW",
1844     "BLUE",
1845     "MAGENTA",
1846     "CYAN",
1847     "WHITE"
1848 };
1849
1850 static void
1851 show_color_name(int y, int x, int color, bool wide)
1852 {
1853     if (move(y, x) != ERR) {
1854         char temp[80];
1855         int width = 8;
1856
1857         if (wide) {
1858             sprintf(temp, "%02d", color);
1859             width = 4;
1860         } else if (color >= 8) {
1861             sprintf(temp, "[%02d]", color);
1862         } else if (color < 0) {
1863             strcpy(temp, "default");
1864         } else {
1865             strcpy(temp, the_color_names[color]);
1866         }
1867         printw("%-*.*s", width, width, temp);
1868     }
1869 }
1870
1871 static void
1872 color_legend(WINDOW *helpwin, bool wide)
1873 {
1874     int row = 1;
1875     int col = 1;
1876
1877     mvwprintw(helpwin, row++, col,
1878               "ESC to exit.");
1879     ++row;
1880     mvwprintw(helpwin, row++, col,
1881               "Use up/down arrow to scroll through the display if it is");
1882     mvwprintw(helpwin, row++, col,
1883               "longer than one screen. Control/N and Control/P can be used");
1884     mvwprintw(helpwin, row++, col,
1885               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
1886     mvwprintw(helpwin, row++, col,
1887               "full screen; control/B and control/F can be used here.");
1888     ++row;
1889     mvwprintw(helpwin, row++, col,
1890               "Toggles:");
1891     mvwprintw(helpwin, row++, col,
1892               "  a/A     toggle altcharset off/on");
1893     mvwprintw(helpwin, row++, col,
1894               "  b/B     toggle bold off/on");
1895     mvwprintw(helpwin, row++, col,
1896               "  n/N     toggle text/number on/off");
1897     mvwprintw(helpwin, row++, col,
1898               "  r/R     toggle reverse on/off");
1899     mvwprintw(helpwin, row++, col,
1900               "  w/W     toggle width between 8/16 colors");
1901 #if USE_WIDEC_SUPPORT
1902     if (wide) {
1903         mvwprintw(helpwin, row++, col,
1904                   "Wide characters:");
1905         mvwprintw(helpwin, row++, col,
1906                   "  x/X     toggle text between ASCII and wide-character");
1907     }
1908 #else
1909     (void) wide;
1910 #endif
1911 }
1912
1913 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1914
1915 /* generate a color test pattern */
1916 static void
1917 color_test(void)
1918 {
1919     short i;
1920     int top = 0, width;
1921     int base_row = 0;
1922     int grid_top = top + 3;
1923     int page_size = (LINES - grid_top);
1924     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1925     int row_limit;
1926     int per_row;
1927     char numbered[80];
1928     const char *hello;
1929     bool done = FALSE;
1930     bool opt_acsc = FALSE;
1931     bool opt_bold = FALSE;
1932     bool opt_revs = FALSE;
1933     bool opt_nums = FALSE;
1934     bool opt_wide = FALSE;
1935     WINDOW *helpwin;
1936
1937     if (COLORS * COLORS == COLOR_PAIRS) {
1938         int limit = (COLORS - min_colors) * (COLORS - min_colors);
1939         if (pairs_max > limit)
1940             pairs_max = limit;
1941     } else {
1942         if (pairs_max > COLOR_PAIRS)
1943             pairs_max = COLOR_PAIRS;
1944     }
1945
1946     while (!done) {
1947         int shown = 0;
1948
1949         /* this assumes an 80-column line */
1950         if (opt_wide) {
1951             width = 4;
1952             hello = "Test";
1953             per_row = (COLORS > 8) ? 16 : 8;
1954         } else {
1955             width = 8;
1956             hello = "Hello";
1957             per_row = 8;
1958         }
1959         per_row -= min_colors;
1960
1961         row_limit = (pairs_max + per_row - 1) / per_row;
1962
1963         move(0, 0);
1964         (void) printw("There are %d color pairs and %d colors%s\n",
1965                       pairs_max, COLORS,
1966                       min_colors ? " besides 'default'" : "");
1967
1968         clrtobot();
1969         (void) mvprintw(top + 1, 0,
1970                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1971                         row_limit,
1972                         per_row,
1973                         opt_bold ? "on" : "off");
1974
1975         /* show color names/numbers across the top */
1976         for (i = 0; i < per_row; i++)
1977             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
1978
1979         /* show a grid of colors, with color names/ numbers on the left */
1980         for (i = (short) (base_row * per_row); i < pairs_max; i++) {
1981             int row = grid_top + (i / per_row) - base_row;
1982             int col = (i % per_row + 1) * width;
1983             short pair = i;
1984
1985 #define InxToFG(i) (short) ((i % (COLORS - min_colors)) + min_colors)
1986 #define InxToBG(i) (short) ((i / (COLORS - min_colors)) + min_colors)
1987             if (row >= 0 && move(row, col) != ERR) {
1988                 short fg = InxToFG(i);
1989                 short bg = InxToBG(i);
1990
1991                 init_pair(pair, fg, bg);
1992                 attron((attr_t) COLOR_PAIR(pair));
1993                 if (opt_acsc)
1994                     attron((attr_t) A_ALTCHARSET);
1995                 if (opt_bold)
1996                     attron((attr_t) A_BOLD);
1997                 if (opt_revs)
1998                     attron((attr_t) A_REVERSE);
1999
2000                 if (opt_nums) {
2001                     sprintf(numbered, "{%02X}", i);
2002                     hello = numbered;
2003                 }
2004                 printw("%-*.*s", width, width, hello);
2005                 attrset(A_NORMAL);
2006
2007                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2008                     show_color_name(row, 0, InxToBG(i), opt_wide);
2009                 }
2010                 ++shown;
2011             } else if (shown) {
2012                 break;
2013             }
2014         }
2015
2016         switch (wGetchar(stdscr)) {
2017         case 'a':
2018             opt_acsc = FALSE;
2019             break;
2020         case 'A':
2021             opt_acsc = TRUE;
2022             break;
2023         case 'b':
2024             opt_bold = FALSE;
2025             break;
2026         case 'B':
2027             opt_bold = TRUE;
2028             break;
2029         case 'n':
2030             opt_nums = FALSE;
2031             break;
2032         case 'N':
2033             opt_nums = TRUE;
2034             break;
2035         case 'r':
2036             opt_revs = FALSE;
2037             break;
2038         case 'R':
2039             opt_revs = TRUE;
2040             break;
2041         case case_QUIT:
2042             done = TRUE;
2043             continue;
2044         case 'w':
2045             set_color_test(opt_wide, FALSE);
2046             break;
2047         case 'W':
2048             set_color_test(opt_wide, TRUE);
2049             break;
2050         case CTRL('p'):
2051         case KEY_UP:
2052             if (base_row <= 0) {
2053                 beep();
2054             } else {
2055                 base_row -= 1;
2056             }
2057             break;
2058         case CTRL('n'):
2059         case KEY_DOWN:
2060             if (base_row + page_size >= row_limit) {
2061                 beep();
2062             } else {
2063                 base_row += 1;
2064             }
2065             break;
2066         case CTRL('b'):
2067         case KEY_PREVIOUS:
2068         case KEY_PPAGE:
2069             if (base_row <= 0) {
2070                 beep();
2071             } else {
2072                 base_row -= (page_size - 1);
2073                 if (base_row < 0)
2074                     base_row = 0;
2075             }
2076             break;
2077         case CTRL('f'):
2078         case KEY_NEXT:
2079         case KEY_NPAGE:
2080             if (base_row + page_size >= row_limit) {
2081                 beep();
2082             } else {
2083                 base_row += page_size - 1;
2084                 if (base_row + page_size >= row_limit) {
2085                     base_row = row_limit - page_size - 1;
2086                 }
2087             }
2088             break;
2089         case '?':
2090             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2091                 box(helpwin, 0, 0);
2092                 color_legend(helpwin, FALSE);
2093                 wGetchar(helpwin);
2094                 delwin(helpwin);
2095             }
2096             break;
2097         default:
2098             beep();
2099             continue;
2100         }
2101     }
2102
2103     erase();
2104     endwin();
2105 }
2106
2107 #if USE_WIDEC_SUPPORT
2108 /* generate a color test pattern */
2109 static void
2110 wide_color_test(void)
2111 {
2112     int c;
2113     int i;
2114     int top = 0, width;
2115     int base_row = 0;
2116     int grid_top = top + 3;
2117     int page_size = (LINES - grid_top);
2118     int pairs_max = (unsigned short) (-1);
2119     int row_limit;
2120     int per_row;
2121     char numbered[80];
2122     const char *hello;
2123     bool done = FALSE;
2124     bool opt_acsc = FALSE;
2125     bool opt_bold = FALSE;
2126     bool opt_revs = FALSE;
2127     bool opt_wide = FALSE;
2128     bool opt_nums = FALSE;
2129     bool opt_xchr = FALSE;
2130     wchar_t buffer[10];
2131     WINDOW *helpwin;
2132
2133     if (COLORS * COLORS == COLOR_PAIRS) {
2134         int limit = (COLORS - min_colors) * (COLORS - min_colors);
2135         if (pairs_max > limit)
2136             pairs_max = limit;
2137     } else {
2138         if (pairs_max > COLOR_PAIRS)
2139             pairs_max = COLOR_PAIRS;
2140     }
2141
2142     while (!done) {
2143         int shown = 0;
2144
2145         /* this assumes an 80-column line */
2146         if (opt_wide) {
2147             width = 4;
2148             hello = "Test";
2149             per_row = (COLORS > 8) ? 16 : 8;
2150         } else {
2151             width = 8;
2152             hello = "Hello";
2153             per_row = 8;
2154         }
2155         per_row -= min_colors;
2156
2157         if (opt_xchr) {
2158             make_fullwidth_text(buffer, hello);
2159             width *= 2;
2160             per_row /= 2;
2161         } else {
2162             make_narrow_text(buffer, hello);
2163         }
2164
2165         row_limit = (pairs_max + per_row - 1) / per_row;
2166
2167         move(0, 0);
2168         (void) printw("There are %d color pairs and %d colors%s\n",
2169                       pairs_max, COLORS,
2170                       min_colors ? " besides 'default'" : "");
2171
2172         clrtobot();
2173         (void) mvprintw(top + 1, 0,
2174                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
2175                         row_limit,
2176                         per_row,
2177                         opt_bold ? "on" : "off");
2178
2179         /* show color names/numbers across the top */
2180         for (i = 0; i < per_row; i++)
2181             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2182
2183         /* show a grid of colors, with color names/ numbers on the left */
2184         for (i = (base_row * per_row); i < pairs_max; i++) {
2185             int row = grid_top + (i / per_row) - base_row;
2186             int col = (i % per_row + 1) * width;
2187             short pair = (short) i;
2188
2189             if (row >= 0 && move(row, col) != ERR) {
2190                 init_pair(pair, InxToFG(i), InxToBG(i));
2191                 color_set(pair, NULL);
2192                 if (opt_acsc)
2193                     attr_on((attr_t) A_ALTCHARSET, NULL);
2194                 if (opt_bold)
2195                     attr_on((attr_t) A_BOLD, NULL);
2196                 if (opt_revs)
2197                     attr_on((attr_t) A_REVERSE, NULL);
2198
2199                 if (opt_nums) {
2200                     sprintf(numbered, "{%02X}", i);
2201                     if (opt_xchr) {
2202                         make_fullwidth_text(buffer, numbered);
2203                     } else {
2204                         make_narrow_text(buffer, numbered);
2205                     }
2206                 }
2207                 addnwstr(buffer, width);
2208                 attr_set(A_NORMAL, 0, NULL);
2209
2210                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2211                     show_color_name(row, 0, InxToBG(i), opt_wide);
2212                 }
2213                 ++shown;
2214             } else if (shown) {
2215                 break;
2216             }
2217         }
2218
2219         switch (c = wGetchar(stdscr)) {
2220         case 'a':
2221             opt_acsc = FALSE;
2222             break;
2223         case 'A':
2224             opt_acsc = TRUE;
2225             break;
2226         case 'b':
2227             opt_bold = FALSE;
2228             break;
2229         case 'B':
2230             opt_bold = TRUE;
2231             break;
2232         case 'n':
2233             opt_nums = FALSE;
2234             break;
2235         case 'N':
2236             opt_nums = TRUE;
2237             break;
2238         case 'r':
2239             opt_revs = FALSE;
2240             break;
2241         case 'R':
2242             opt_revs = TRUE;
2243             break;
2244         case case_QUIT:
2245             done = TRUE;
2246             continue;
2247         case 'w':
2248             set_color_test(opt_wide, FALSE);
2249             break;
2250         case 'W':
2251             set_color_test(opt_wide, TRUE);
2252             break;
2253         case 'x':
2254             opt_xchr = FALSE;
2255             break;
2256         case 'X':
2257             opt_xchr = TRUE;
2258             break;
2259         case CTRL('p'):
2260         case KEY_UP:
2261             if (base_row <= 0) {
2262                 beep();
2263             } else {
2264                 base_row -= 1;
2265             }
2266             break;
2267         case CTRL('n'):
2268         case KEY_DOWN:
2269             if (base_row + page_size >= row_limit) {
2270                 beep();
2271             } else {
2272                 base_row += 1;
2273             }
2274             break;
2275         case CTRL('b'):
2276         case KEY_PREVIOUS:
2277         case KEY_PPAGE:
2278             if (base_row <= 0) {
2279                 beep();
2280             } else {
2281                 base_row -= (page_size - 1);
2282                 if (base_row < 0)
2283                     base_row = 0;
2284             }
2285             break;
2286         case CTRL('f'):
2287         case KEY_NEXT:
2288         case KEY_NPAGE:
2289             if (base_row + page_size >= row_limit) {
2290                 beep();
2291             } else {
2292                 base_row += page_size - 1;
2293                 if (base_row + page_size >= row_limit) {
2294                     base_row = row_limit - page_size - 1;
2295                 }
2296             }
2297             break;
2298         case '?':
2299             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2300                 box(helpwin, 0, 0);
2301                 color_legend(helpwin, TRUE);
2302                 wGetchar(helpwin);
2303                 delwin(helpwin);
2304             }
2305             break;
2306         default:
2307             beep();
2308             continue;
2309         }
2310     }
2311
2312     erase();
2313     endwin();
2314 }
2315 #endif /* USE_WIDEC_SUPPORT */
2316
2317 static void
2318 change_color(short current, int field, int value, int usebase)
2319 {
2320     short red, green, blue;
2321
2322     color_content(current, &red, &green, &blue);
2323
2324     switch (field) {
2325     case 0:
2326         red = (short) (usebase ? (red + value) : value);
2327         break;
2328     case 1:
2329         green = (short) (usebase ? (green + value) : value);
2330         break;
2331     case 2:
2332         blue = (short) (usebase ? (blue + value) : value);
2333         break;
2334     }
2335
2336     if (init_color(current, red, green, blue) == ERR)
2337         beep();
2338 }
2339
2340 static void
2341 init_all_colors(void)
2342 {
2343     short c;
2344
2345     for (c = 0; c < COLORS; ++c)
2346         init_color(c,
2347                    all_colors[c].red,
2348                    all_colors[c].green,
2349                    all_colors[c].blue);
2350 }
2351
2352 #define scaled_rgb(n) ((255 * (n)) / 1000)
2353
2354 static void
2355 color_edit(void)
2356 /* display the color test pattern, without trying to edit colors */
2357 {
2358     int i;
2359     int current = 0;
2360     int this_c = 0, value = 0, field = 0;
2361     int last_c;
2362     int top_color = 0;
2363     int page_size = (LINES - 6);
2364
2365     init_all_colors();
2366     refresh();
2367
2368     for (i = 0; i < max_colors; i++)
2369         init_pair((short) i, (short) COLOR_WHITE, (short) i);
2370
2371     mvprintw(LINES - 2, 0, "Number: %d", value);
2372
2373     do {
2374         short red, green, blue;
2375
2376         attron(A_BOLD);
2377         mvaddstr(0, 20, "Color RGB Value Editing");
2378         attroff(A_BOLD);
2379
2380         for (i = (short) top_color;
2381              (i - top_color < page_size)
2382              && (i < max_colors); i++) {
2383             char numeric[80];
2384
2385             sprintf(numeric, "[%d]", i);
2386             mvprintw(2 + i - top_color, 0, "%c %-8s:",
2387                      (i == current ? '>' : ' '),
2388                      (i < (int) SIZEOF(the_color_names)
2389                       ? the_color_names[i] : numeric));
2390             attrset(COLOR_PAIR(i));
2391             addstr("        ");
2392             attrset(A_NORMAL);
2393
2394             color_content((short) i, &red, &green, &blue);
2395             addstr("   R = ");
2396             if (current == i && field == 0)
2397                 attron(A_STANDOUT);
2398             printw("%04d", red);
2399             if (current == i && field == 0)
2400                 attrset(A_NORMAL);
2401             addstr(", G = ");
2402             if (current == i && field == 1)
2403                 attron(A_STANDOUT);
2404             printw("%04d", green);
2405             if (current == i && field == 1)
2406                 attrset(A_NORMAL);
2407             addstr(", B = ");
2408             if (current == i && field == 2)
2409                 attron(A_STANDOUT);
2410             printw("%04d", blue);
2411             if (current == i && field == 2)
2412                 attrset(A_NORMAL);
2413             attrset(A_NORMAL);
2414             printw(" ( %3d %3d %3d )",
2415                    scaled_rgb(red),
2416                    scaled_rgb(green),
2417                    scaled_rgb(blue));
2418         }
2419
2420         mvaddstr(LINES - 3, 0,
2421                  "Use up/down to select a color, left/right to change fields.");
2422         mvaddstr(LINES - 2, 0,
2423                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2424
2425         move(2 + current - top_color, 0);
2426
2427         last_c = this_c;
2428         this_c = Getchar();
2429         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2430             value = 0;
2431
2432         switch (this_c) {
2433         case CTRL('b'):
2434         case KEY_PPAGE:
2435             if (current > 0)
2436                 current -= (page_size - 1);
2437             else
2438                 beep();
2439             break;
2440
2441         case CTRL('f'):
2442         case KEY_NPAGE:
2443             if (current < (max_colors - 1))
2444                 current += (page_size - 1);
2445             else
2446                 beep();
2447             break;
2448
2449         case CTRL('p'):
2450         case KEY_UP:
2451             current = (current == 0 ? (max_colors - 1) : current - 1);
2452             break;
2453
2454         case CTRL('n'):
2455         case KEY_DOWN:
2456             current = (current == (max_colors - 1) ? 0 : current + 1);
2457             break;
2458
2459         case KEY_RIGHT:
2460             field = (field == 2 ? 0 : field + 1);
2461             break;
2462
2463         case KEY_LEFT:
2464             field = (field == 0 ? 2 : field - 1);
2465             break;
2466
2467         case '0':
2468         case '1':
2469         case '2':
2470         case '3':
2471         case '4':
2472         case '5':
2473         case '6':
2474         case '7':
2475         case '8':
2476         case '9':
2477             value = value * 10 + (this_c - '0');
2478             break;
2479
2480         case '+':
2481             change_color((short) current, field, value, 1);
2482             break;
2483
2484         case '-':
2485             change_color((short) current, field, -value, 1);
2486             break;
2487
2488         case '=':
2489             change_color((short) current, field, value, 0);
2490             break;
2491
2492         case '?':
2493             erase();
2494             P("                      RGB Value Editing Help");
2495             P("");
2496             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2497             P("the fields in one of the RGB triples of the current colors; the one");
2498             P("currently selected will be reverse-video highlighted.");
2499             P("");
2500             P("To change a field, enter the digits of the new value; they are echoed");
2501             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2502             P("To increment or decrement a value, use the same procedure, but finish");
2503             P("with a `+' or `-'.");
2504             P("");
2505             P("Press 'm' to invoke the top-level menu with the current color settings.");
2506             P("To quit, do ESC");
2507
2508             Pause();
2509             erase();
2510             break;
2511
2512         case 'm':
2513             endwin();
2514             main_menu(FALSE);
2515             refresh();
2516             break;
2517
2518         case case_QUIT:
2519             break;
2520
2521         default:
2522             beep();
2523             break;
2524         }
2525
2526         if (current < 0)
2527             current = 0;
2528         if (current >= max_colors)
2529             current = max_colors - 1;
2530         if (current < top_color)
2531             top_color = current;
2532         if (current - top_color >= page_size)
2533             top_color = current - (page_size - 1);
2534
2535         mvprintw(LINES - 1, 0, "Number: %d", value);
2536         clrtoeol();
2537     } while
2538         (!isQuit(this_c));
2539
2540     erase();
2541
2542     /*
2543      * ncurses does not reset each color individually when calling endwin().
2544      */
2545     init_all_colors();
2546
2547     endwin();
2548 }
2549
2550 /****************************************************************************
2551  *
2552  * Soft-key label test
2553  *
2554  ****************************************************************************/
2555
2556 #if USE_SOFTKEYS
2557
2558 #define SLK_HELP 17
2559 #define SLK_WORK (SLK_HELP + 3)
2560
2561 static void
2562 slk_help(void)
2563 {
2564     static const char *table[] =
2565     {
2566         "Available commands are:"
2567         ,""
2568         ,"^L         -- repaint this message and activate soft keys"
2569         ,"a/d        -- activate/disable soft keys"
2570         ,"c          -- set centered format for labels"
2571         ,"l          -- set left-justified format for labels"
2572         ,"r          -- set right-justified format for labels"
2573         ,"[12345678] -- set label; labels are numbered 1 through 8"
2574         ,"e          -- erase stdscr (should not erase labels)"
2575         ,"s          -- test scrolling of shortened screen"
2576 #if HAVE_SLK_COLOR
2577         ,"F/B        -- cycle through foreground/background colors"
2578 #endif
2579         ,"ESC        -- return to main menu"
2580         ,""
2581         ,"Note: if activating the soft keys causes your terminal to scroll up"
2582         ,"one line, your terminal auto-scrolls when anything is written to the"
2583         ,"last screen position.  The ncurses code does not yet handle this"
2584         ,"gracefully."
2585     };
2586     unsigned j;
2587
2588     move(2, 0);
2589     for (j = 0; j < SIZEOF(table); ++j) {
2590         P(table[j]);
2591     }
2592     refresh();
2593 }
2594
2595 #if HAVE_SLK_COLOR
2596 static void
2597 call_slk_color(short fg, short bg)
2598 {
2599     init_pair(1, bg, fg);
2600     slk_color(1);
2601     mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2602     clrtoeol();
2603     refresh();
2604 }
2605 #endif
2606
2607 static void
2608 slk_test(void)
2609 /* exercise the soft keys */
2610 {
2611     int c, fmt = 1;
2612     char buf[9];
2613     char *s;
2614 #if HAVE_SLK_COLOR
2615     short fg = COLOR_BLACK;
2616     short bg = COLOR_WHITE;
2617 #endif
2618
2619     c = CTRL('l');
2620 #if HAVE_SLK_COLOR
2621     if (use_colors) {
2622         call_slk_color(fg, bg);
2623     }
2624 #endif
2625
2626     do {
2627         move(0, 0);
2628         switch (c) {
2629         case CTRL('l'):
2630             erase();
2631             attron(A_BOLD);
2632             mvaddstr(0, 20, "Soft Key Exerciser");
2633             attroff(A_BOLD);
2634
2635             slk_help();
2636             /* fall through */
2637
2638         case 'a':
2639             slk_restore();
2640             break;
2641
2642         case 'e':
2643             wclear(stdscr);
2644             break;
2645
2646         case 's':
2647             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2648             while ((c = Getchar()) != 'Q' && (c != ERR))
2649                 addch((chtype) c);
2650             break;
2651
2652         case 'd':
2653             slk_clear();
2654             break;
2655
2656         case 'l':
2657             fmt = 0;
2658             break;
2659
2660         case 'c':
2661             fmt = 1;
2662             break;
2663
2664         case 'r':
2665             fmt = 2;
2666             break;
2667
2668         case '1':
2669         case '2':
2670         case '3':
2671         case '4':
2672         case '5':
2673         case '6':
2674         case '7':
2675         case '8':
2676             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2677             strcpy(buf, "");
2678             if ((s = slk_label(c - '0')) != 0) {
2679                 strncpy(buf, s, 8);
2680             }
2681             wGetstring(stdscr, buf, 8);
2682             slk_set((c - '0'), buf, fmt);
2683             slk_refresh();
2684             move(SLK_WORK, 0);
2685             clrtobot();
2686             break;
2687
2688         case case_QUIT:
2689             goto done;
2690
2691 #if HAVE_SLK_COLOR
2692         case 'F':
2693             if (use_colors) {
2694                 fg = (short) ((fg + 1) % COLORS);
2695                 call_slk_color(fg, bg);
2696             }
2697             break;
2698         case 'B':
2699             if (use_colors) {
2700                 bg = (short) ((bg + 1) % COLORS);
2701                 call_slk_color(fg, bg);
2702             }
2703             break;
2704 #endif
2705 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2706         case KEY_RESIZE:
2707             wnoutrefresh(stdscr);
2708             break;
2709 #endif
2710
2711         default:
2712             beep();
2713         }
2714     } while (!isQuit(c = Getchar()));
2715
2716   done:
2717     slk_clear();
2718     erase();
2719     endwin();
2720 }
2721
2722 #if USE_WIDEC_SUPPORT
2723 #define SLKLEN 8
2724 static void
2725 wide_slk_test(void)
2726 /* exercise the soft keys */
2727 {
2728     int c, fmt = 1;
2729     wchar_t buf[SLKLEN + 1];
2730     char *s;
2731     short fg = COLOR_BLACK;
2732     short bg = COLOR_WHITE;
2733
2734     c = CTRL('l');
2735     if (use_colors) {
2736         call_slk_color(fg, bg);
2737     }
2738     do {
2739         move(0, 0);
2740         switch (c) {
2741         case CTRL('l'):
2742             erase();
2743             attr_on(WA_BOLD, NULL);
2744             mvaddstr(0, 20, "Soft Key Exerciser");
2745             attr_off(WA_BOLD, NULL);
2746
2747             slk_help();
2748             /* fall through */
2749
2750         case 'a':
2751             slk_restore();
2752             break;
2753
2754         case 'e':
2755             wclear(stdscr);
2756             break;
2757
2758         case 's':
2759             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2760             while ((c = Getchar()) != 'Q' && (c != ERR))
2761                 addch((chtype) c);
2762             break;
2763
2764         case 'd':
2765             slk_clear();
2766             break;
2767
2768         case 'l':
2769             fmt = 0;
2770             break;
2771
2772         case 'c':
2773             fmt = 1;
2774             break;
2775
2776         case 'r':
2777             fmt = 2;
2778             break;
2779
2780         case '1':
2781         case '2':
2782         case '3':
2783         case '4':
2784         case '5':
2785         case '6':
2786         case '7':
2787         case '8':
2788             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2789             *buf = 0;
2790             if ((s = slk_label(c - '0')) != 0) {
2791                 char *temp = strdup(s);
2792                 size_t used = strlen(temp);
2793                 size_t want = SLKLEN;
2794                 size_t test;
2795 #ifndef state_unused
2796                 mbstate_t state;
2797 #endif
2798
2799                 buf[0] = L'\0';
2800                 while (want > 0 && used != 0) {
2801                     const char *base = s;
2802                     reset_mbytes(state);
2803                     test = count_mbytes(base, 0, &state);
2804                     if (test == (size_t) -1) {
2805                         temp[--used] = 0;
2806                     } else if (test > want) {
2807                         temp[--used] = 0;
2808                     } else {
2809                         reset_mbytes(state);
2810                         trans_mbytes(buf, base, want, &state);
2811                         break;
2812                     }
2813                 }
2814                 free(temp);
2815             }
2816             wGet_wstring(stdscr, buf, SLKLEN);
2817             slk_wset((c - '0'), buf, fmt);
2818             slk_refresh();
2819             move(SLK_WORK, 0);
2820             clrtobot();
2821             break;
2822
2823         case case_QUIT:
2824             goto done;
2825
2826         case 'F':
2827             if (use_colors) {
2828                 fg = (short) ((fg + 1) % COLORS);
2829                 call_slk_color(fg, bg);
2830             }
2831             break;
2832         case 'B':
2833             if (use_colors) {
2834                 bg = (short) ((bg + 1) % COLORS);
2835                 call_slk_color(fg, bg);
2836             }
2837             break;
2838 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2839         case KEY_RESIZE:
2840             wnoutrefresh(stdscr);
2841             break;
2842 #endif
2843         default:
2844             beep();
2845         }
2846     } while (!isQuit(c = Getchar()));
2847
2848   done:
2849     slk_clear();
2850     erase();
2851     endwin();
2852 }
2853 #endif
2854 #endif /* SLK_INIT */
2855
2856 /****************************************************************************
2857  *
2858  * Alternate character-set stuff
2859  *
2860  ****************************************************************************/
2861 /* *INDENT-OFF* */
2862 static struct {
2863     chtype attr;
2864     const char *name;
2865 } attrs_to_cycle[] = {
2866     { A_NORMAL,         "normal" },
2867     { A_BOLD,           "bold" },
2868     { A_BLINK,          "blink" },
2869     { A_REVERSE,        "reverse" },
2870     { A_UNDERLINE,      "underline" },
2871 };
2872 /* *INDENT-ON* */
2873
2874 static bool
2875 cycle_attr(int ch, unsigned *at_code, chtype *attr)
2876 {
2877     bool result = TRUE;
2878
2879     switch (ch) {
2880     case 'v':
2881         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
2882             *at_code = 0;
2883         break;
2884     case 'V':
2885         if (*at_code == 1)
2886             *at_code = SIZEOF(attrs_to_cycle) - 1;
2887         else
2888             *at_code -= 1;
2889         break;
2890     default:
2891         result = FALSE;
2892         break;
2893     }
2894     if (result)
2895         *attr = attrs_to_cycle[*at_code].attr;
2896     return result;
2897 }
2898
2899 static bool
2900 cycle_colors(int ch, int *fg, int *bg, short *pair)
2901 {
2902     bool result = FALSE;
2903
2904     if (use_colors) {
2905         result = TRUE;
2906         switch (ch) {
2907         case 'F':
2908             if ((*fg -= 1) < 0)
2909                 *fg = COLORS - 1;
2910             break;
2911         case 'f':
2912             if ((*fg += 1) >= COLORS)
2913                 *fg = 0;
2914             break;
2915         case 'B':
2916             if ((*bg -= 1) < 0)
2917                 *bg = COLORS - 1;
2918             break;
2919         case 'b':
2920             if ((*bg += 1) >= COLORS)
2921                 *bg = 0;
2922             break;
2923         default:
2924             result = FALSE;
2925             break;
2926         }
2927         if (result) {
2928             *pair = (short) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2929             if (*pair != 0) {
2930                 *pair = 1;
2931                 if (init_pair(*pair, (short) *fg, (short) *bg) == ERR) {
2932                     result = FALSE;
2933                 }
2934             }
2935         }
2936     }
2937     return result;
2938 }
2939
2940 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2941  * terminal to perform functions.  The remaining codes can be graphic.
2942  */
2943 static void
2944 show_upper_chars(unsigned first, int repeat, attr_t attr, short pair)
2945 {
2946     bool C1 = (first == 128);
2947     unsigned code;
2948     unsigned last = first + 31;
2949     int reply;
2950
2951     erase();
2952     attron(A_BOLD);
2953     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2954              C1 ? "C1" : "GR", first, last);
2955     attroff(A_BOLD);
2956     refresh();
2957
2958     for (code = first; code <= last; code++) {
2959         int count = repeat;
2960         int row = 2 + ((int) (code - first) % 16);
2961         int col = ((int) (code - first) / 16) * COLS / 2;
2962         char tmp[80];
2963         sprintf(tmp, "%3u (0x%x)", code, code);
2964         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2965
2966         do {
2967             if (C1)
2968                 nodelay(stdscr, TRUE);
2969             echochar(colored_chtype(code, attr, pair));
2970             if (C1) {
2971                 /* (yes, this _is_ crude) */
2972                 while ((reply = Getchar()) != ERR) {
2973                     addch(UChar(reply));
2974                     napms(10);
2975                 }
2976                 nodelay(stdscr, FALSE);
2977             }
2978         } while (--count > 0);
2979     }
2980 }
2981
2982 #define PC_COLS 4
2983
2984 static void
2985 show_pc_chars(int repeat, attr_t attr, short pair)
2986 {
2987     unsigned code;
2988
2989     erase();
2990     attron(A_BOLD);
2991     mvprintw(0, 20, "Display of PC Character Codes");
2992     attroff(A_BOLD);
2993     refresh();
2994
2995     for (code = 0; code < 16; ++code) {
2996         mvprintw(2, (int) code * PC_COLS + 8, "%X", code);
2997     }
2998     for (code = 0; code < 256; code++) {
2999         int count = repeat;
3000         int row = 3 + (int) (code / 16) + (code >= 128);
3001         int col = 8 + (int) (code % 16) * PC_COLS;
3002         if ((code % 16) == 0)
3003             mvprintw(row, 0, "0x%02x:", code);
3004         move(row, col);
3005         do {
3006             switch (code) {
3007             case '\n':
3008             case '\r':
3009             case '\b':
3010             case '\f':
3011             case '\033':
3012             case 0x9b:
3013                 /*
3014                  * Skip the ones that do not work.
3015                  */
3016                 break;
3017             default:
3018                 addch(colored_chtype(code, A_ALTCHARSET | attr, pair));
3019                 break;
3020             }
3021         } while (--count > 0);
3022     }
3023 }
3024
3025 static void
3026 show_box_chars(int repeat, attr_t attr, short pair)
3027 {
3028     (void) repeat;
3029     attr |= COLOR_PAIR(pair);
3030
3031     erase();
3032     attron(A_BOLD);
3033     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
3034     attroff(A_BOLD);
3035     refresh();
3036     /* *INDENT-OFF* */
3037     wborder(stdscr,
3038             colored_chtype(ACS_VLINE,    attr, pair),
3039             colored_chtype(ACS_VLINE,    attr, pair),
3040             colored_chtype(ACS_HLINE,    attr, pair),
3041             colored_chtype(ACS_HLINE,    attr, pair),
3042             colored_chtype(ACS_ULCORNER, attr, pair),
3043             colored_chtype(ACS_URCORNER, attr, pair),
3044             colored_chtype(ACS_LLCORNER, attr, pair),
3045             colored_chtype(ACS_LRCORNER, attr, pair));
3046     mvhline(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3047     mvvline(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3048     mvaddch(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3049     mvaddch(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3050     mvaddch(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3051     mvaddch(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3052     mvaddch(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3053     /* *INDENT-ON* */
3054
3055 }
3056
3057 static int
3058 show_1_acs(int n, int repeat, const char *name, chtype code)
3059 {
3060     const int height = 16;
3061     int row = 2 + (n % height);
3062     int col = (n / height) * COLS / 2;
3063
3064     mvprintw(row, col, "%*s : ", COLS / 4, name);
3065     do {
3066         addch(code);
3067     } while (--repeat > 0);
3068     return n + 1;
3069 }
3070
3071 static void
3072 show_acs_chars(int repeat, attr_t attr, short pair)
3073 /* display the ACS character set */
3074 {
3075     int n;
3076
3077 #define BOTH(name) #name, colored_chtype(name, attr, pair)
3078
3079     erase();
3080     attron(A_BOLD);
3081     mvaddstr(0, 20, "Display of the ACS Character Set");
3082     attroff(A_BOLD);
3083     refresh();
3084
3085     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3086     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3087     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3088     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3089
3090     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3091     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3092     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3093     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3094
3095     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3096     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3097
3098     /*
3099      * HPUX's ACS definitions are broken here.  Just give up.
3100      */
3101 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3102     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3103     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3104     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3105     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3106
3107     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3108     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3109     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3110     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3111     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3112     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3113     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3114     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3115     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3116
3117     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3118     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3119     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3120
3121     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3122     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3123     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3124     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3125     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3126     n = show_1_acs(n, repeat, BOTH(ACS_S9));
3127 #endif
3128 }
3129
3130 static void
3131 acs_display(void)
3132 {
3133     int c = 'a';
3134     char *term = getenv("TERM");
3135     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3136                               ? "p=PC, "
3137                               : "");
3138     chtype attr = A_NORMAL;
3139     int digit = 0;
3140     int repeat = 1;
3141     int fg = COLOR_BLACK;
3142     int bg = COLOR_BLACK;
3143     unsigned at_code = 0;
3144     short pair = 0;
3145     void (*last_show_acs) (int, attr_t, short) = 0;
3146
3147     do {
3148         switch (c) {
3149         case CTRL('L'):
3150             Repaint();
3151             break;
3152         case 'a':
3153             ToggleAcs(last_show_acs, show_acs_chars);
3154             break;
3155         case 'p':
3156             if (*pch_kludge)
3157                 ToggleAcs(last_show_acs, show_pc_chars);
3158             else
3159                 beep();
3160             break;
3161         case 'x':
3162             ToggleAcs(last_show_acs, show_box_chars);
3163             break;
3164         case '0':
3165         case '1':
3166         case '2':
3167         case '3':
3168             digit = (c - '0');
3169             last_show_acs = 0;
3170             break;
3171         case '-':
3172             if (digit > 0) {
3173                 --digit;
3174                 last_show_acs = 0;
3175             } else {
3176                 beep();
3177             }
3178             break;
3179         case '+':
3180             if (digit < 3) {
3181                 ++digit;
3182                 last_show_acs = 0;
3183             } else {
3184                 beep();
3185             }
3186             break;
3187         case '>':
3188             if (repeat < (COLS / 4))
3189                 ++repeat;
3190             break;
3191         case '<':
3192             if (repeat > 1)
3193                 --repeat;
3194             break;
3195         default:
3196             if (cycle_attr(c, &at_code, &attr)
3197                 || cycle_colors(c, &fg, &bg, &pair)) {
3198                 break;
3199             } else {
3200                 beep();
3201             }
3202             break;
3203         }
3204         if (last_show_acs != 0)
3205             last_show_acs(repeat, attr, pair);
3206         else
3207             show_upper_chars((unsigned) (digit * 32 + 128), repeat, attr, pair);
3208
3209         mvprintw(LINES - 3, 0,
3210                  "Note: ANSI terminals may not display C1 characters.");
3211         mvprintw(LINES - 2, 0,
3212                  "Select: a=ACS, x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3213                  pch_kludge);
3214         if (use_colors) {
3215             mvprintw(LINES - 1, 0,
3216                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3217                      attrs_to_cycle[at_code].name,
3218                      fg, bg);
3219         } else {
3220             mvprintw(LINES - 1, 0,
3221                      "v/V cycles through video attributes (%s).",
3222                      attrs_to_cycle[at_code].name);
3223         }
3224         refresh();
3225     } while (!isQuit(c = Getchar()));
3226
3227     Pause();
3228     erase();
3229     endwin();
3230 }
3231
3232 #if USE_WIDEC_SUPPORT
3233 static cchar_t *
3234 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, short pair)
3235 {
3236     int count = getcchar(src, NULL, NULL, NULL, 0);
3237     wchar_t *wch = 0;
3238     attr_t ignore_attr;
3239     short ignore_pair;
3240
3241     *dst = *src;
3242     if (count > 0) {
3243         if ((wch = typeMalloc(wchar_t, (unsigned) count + 1)) != 0) {
3244             if (getcchar(src, wch, &ignore_attr, &ignore_pair, 0) != ERR) {
3245                 attr |= (ignore_attr & A_ALTCHARSET);
3246                 setcchar(dst, wch, attr, pair, 0);
3247             }
3248             free(wch);
3249         }
3250     }
3251     return dst;
3252 }
3253
3254 static void
3255 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
3256 {
3257     cchar_t temp;
3258     wchar_t code;
3259     int last = first + 31;
3260
3261     erase();
3262     attron(A_BOLD);
3263     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
3264     attroff(A_BOLD);
3265
3266     for (code = first; (int) code <= last; code++) {
3267         int row = 2 + ((code - first) % 16);
3268         int col = ((code - first) / 16) * COLS / 2;
3269         wchar_t codes[10];
3270         char tmp[80];
3271         int count = repeat;
3272         int y, x;
3273
3274         memset(&codes, 0, sizeof(codes));
3275         codes[0] = code;
3276         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3277         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
3278         setcchar(&temp, codes, attr, pair, 0);
3279         do {
3280             /*
3281              * Give non-spacing characters something to combine with.  If we
3282              * don't, they'll bunch up in a heap on the space after the ":".
3283              * Mark them with reverse-video to make them simpler to find on
3284              * the display.
3285              */
3286             if (wcwidth(code) == 0)
3287                 addch(space | A_REVERSE);
3288             /*
3289              * This could use add_wch(), but is done for comparison with the
3290              * normal 'f' test (and to make a test-case for echo_wchar()).
3291              * The screen will flicker because the erase() at the top of the
3292              * function is met by the builtin refresh() in echo_wchar().
3293              */
3294             echo_wchar(&temp);
3295             /*
3296              * The repeat-count may make text wrap - avoid that.
3297              */
3298             getyx(stdscr, y, x);
3299             if (x >= col + (COLS / 2) - 2)
3300                 break;
3301         } while (--count > 0);
3302     }
3303 }
3304
3305 static int
3306 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3307 {
3308     const int height = 16;
3309     int row = 2 + (n % height);
3310     int col = (n / height) * COLS / 2;
3311
3312     mvprintw(row, col, "%*s : ", COLS / 4, name);
3313     while (--repeat >= 0) {
3314         add_wch(code);
3315     }
3316     return n + 1;
3317 }
3318
3319 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3320
3321 static void
3322 show_wacs_chars(int repeat, attr_t attr, short pair)
3323 /* display the wide-ACS character set */
3324 {
3325     cchar_t temp;
3326
3327     int n;
3328
3329 /*#define BOTH2(name) #name, &(name) */
3330 #define BOTH2(name) #name, MERGE_ATTR(name)
3331
3332     erase();
3333     attron(A_BOLD);
3334     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3335     attroff(A_BOLD);
3336     refresh();
3337
3338     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3339     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3340     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3341     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3342
3343     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3344     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3345     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3346     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3347
3348     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3349     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3350
3351     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3352     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3353     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3354     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3355
3356     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3357     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3358     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3359     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3360     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3361     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3362     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3363     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3364     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3365
3366 #ifdef CURSES_WACS_ARRAY
3367     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3368     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3369     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3370
3371     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3372     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3373     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3374     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3375     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3376     n = show_1_wacs(n, repeat, BOTH2(WACS_S9));
3377 #endif
3378 }
3379
3380 #undef MERGE_ATTR
3381
3382 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
3383
3384 static void
3385 show_wbox_chars(int repeat, attr_t attr, short pair)
3386 {
3387     cchar_t temp[8];
3388
3389     (void) repeat;
3390     erase();
3391     attron(A_BOLD);
3392     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3393     attroff(A_BOLD);
3394     refresh();
3395
3396     wborder_set(stdscr,
3397                 MERGE_ATTR(0, WACS_VLINE),
3398                 MERGE_ATTR(1, WACS_VLINE),
3399                 MERGE_ATTR(2, WACS_HLINE),
3400                 MERGE_ATTR(3, WACS_HLINE),
3401                 MERGE_ATTR(4, WACS_ULCORNER),
3402                 MERGE_ATTR(5, WACS_URCORNER),
3403                 MERGE_ATTR(6, WACS_LLCORNER),
3404                 MERGE_ATTR(7, WACS_LRCORNER));
3405     /* *INDENT-OFF* */
3406     mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
3407     mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
3408     mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
3409     mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
3410     mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
3411     mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
3412     mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
3413     /* *INDENT-ON* */
3414
3415 }
3416
3417 #undef MERGE_ATTR
3418
3419 static int
3420 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3421 {
3422     const int height = 16;
3423     int row = 2 + (n % height);
3424     int col = (n / height) * COLS / 2;
3425     char temp[80];
3426
3427     mvprintw(row, col, "%*s : ", COLS / 4, name);
3428     attr_set(attr, pair, 0);
3429     addstr(strcpy(temp, code));
3430     attr_set(A_NORMAL, 0, 0);
3431     return n + 1;
3432 }
3433
3434 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3435
3436 static void
3437 show_utf8_chars(int repeat, attr_t attr, short pair)
3438 {
3439     int n;
3440
3441     (void) repeat;
3442     erase();
3443     attron(A_BOLD);
3444     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3445     attroff(A_BOLD);
3446     refresh();
3447     /* *INDENT-OFF* */
3448     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3449     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3450     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3451     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3452
3453     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3454     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3455     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3456     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3457
3458     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3459     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3460
3461     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3462     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3463     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3464     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3465
3466     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3467     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3468     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3469     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3470     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3471     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3472     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3473     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3474     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3475     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3476     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3477     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3478
3479     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3480     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3481     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3482     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3483     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3484     n = SHOW_UTF8(n, "WACS_S9",         "\342\216\275");
3485     /* *INDENT-ON* */
3486
3487 }
3488
3489 /* display the wide-ACS character set */
3490 static void
3491 wide_acs_display(void)
3492 {
3493     int c = 'a';
3494     int digit = 0;
3495     int repeat = 1;
3496     int space = ' ';
3497     chtype attr = A_NORMAL;
3498     int fg = COLOR_BLACK;
3499     int bg = COLOR_BLACK;
3500     unsigned at_code = 0;
3501     short pair = 0;
3502     void (*last_show_wacs) (int, attr_t, short) = 0;
3503
3504     do {
3505         switch (c) {
3506         case CTRL('L'):
3507             Repaint();
3508             break;
3509         case 'a':
3510             ToggleAcs(last_show_wacs, show_wacs_chars);
3511             break;
3512         case 'x':
3513             ToggleAcs(last_show_wacs, show_wbox_chars);
3514             break;
3515         case 'u':
3516             ToggleAcs(last_show_wacs, show_utf8_chars);
3517             break;
3518         default:
3519             if (c < 256 && isdigit(c)) {
3520                 digit = (c - '0');
3521                 last_show_wacs = 0;
3522             } else if (c == '+') {
3523                 ++digit;
3524                 last_show_wacs = 0;
3525             } else if (c == '-' && digit > 0) {
3526                 --digit;
3527                 last_show_wacs = 0;
3528             } else if (c == '>' && repeat < (COLS / 4)) {
3529                 ++repeat;
3530             } else if (c == '<' && repeat > 1) {
3531                 --repeat;
3532             } else if (c == '_') {
3533                 space = (space == ' ') ? '_' : ' ';
3534                 last_show_wacs = 0;
3535             } else if (cycle_attr(c, &at_code, &attr)
3536                        || cycle_colors(c, &fg, &bg, &pair)) {
3537                 if (last_show_wacs != 0)
3538                     break;
3539             } else {
3540                 beep();
3541                 break;
3542             }
3543             break;
3544         }
3545         if (last_show_wacs != 0)
3546             last_show_wacs(repeat, attr, pair);
3547         else
3548             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3549
3550         mvprintw(LINES - 3, 0,
3551                  "Select: a WACS, x box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, ESC=quit");
3552         if (use_colors) {
3553             mvprintw(LINES - 2, 0,
3554                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3555                      attrs_to_cycle[at_code].name,
3556                      fg, bg);
3557         } else {
3558             mvprintw(LINES - 2, 0,
3559                      "v/V cycles through video attributes (%s).",
3560                      attrs_to_cycle[at_code].name);
3561         }
3562         refresh();
3563     } while (!isQuit(c = Getchar()));
3564
3565     Pause();
3566     erase();
3567     endwin();
3568 }
3569
3570 #endif
3571
3572 /*
3573  * Graphic-rendition test (adapted from vttest)
3574  */
3575 static void
3576 test_sgr_attributes(void)
3577 {
3578     int pass;
3579
3580     for (pass = 0; pass < 2; pass++) {
3581         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3582
3583         /* Use non-default colors if possible to exercise bce a little */
3584         if (use_colors) {
3585             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3586             normal |= COLOR_PAIR(1);
3587         }
3588         bkgdset(normal);
3589         erase();
3590         mvprintw(1, 20, "Graphic rendition test pattern:");
3591
3592         mvprintw(4, 1, "vanilla");
3593
3594 #define set_sgr(mask) bkgdset((normal^(mask)));
3595         set_sgr(A_BOLD);
3596         mvprintw(4, 40, "bold");
3597
3598         set_sgr(A_UNDERLINE);
3599         mvprintw(6, 6, "underline");
3600
3601         set_sgr(A_BOLD | A_UNDERLINE);
3602         mvprintw(6, 45, "bold underline");
3603
3604         set_sgr(A_BLINK);
3605         mvprintw(8, 1, "blink");
3606
3607         set_sgr(A_BLINK | A_BOLD);
3608         mvprintw(8, 40, "bold blink");
3609
3610         set_sgr(A_UNDERLINE | A_BLINK);
3611         mvprintw(10, 6, "underline blink");
3612
3613         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3614         mvprintw(10, 45, "bold underline blink");
3615
3616         set_sgr(A_REVERSE);
3617         mvprintw(12, 1, "negative");
3618
3619         set_sgr(A_BOLD | A_REVERSE);
3620         mvprintw(12, 40, "bold negative");
3621
3622         set_sgr(A_UNDERLINE | A_REVERSE);
3623         mvprintw(14, 6, "underline negative");
3624
3625         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3626         mvprintw(14, 45, "bold underline negative");
3627
3628         set_sgr(A_BLINK | A_REVERSE);
3629         mvprintw(16, 1, "blink negative");
3630
3631         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3632         mvprintw(16, 40, "bold blink negative");
3633
3634         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3635         mvprintw(18, 6, "underline blink negative");
3636
3637         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3638         mvprintw(18, 45, "bold underline blink negative");
3639
3640         bkgdset(normal);
3641         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3642                  "Light");
3643         clrtoeol();
3644         Pause();
3645     }
3646
3647     bkgdset(A_NORMAL | BLANK);
3648     erase();
3649     endwin();
3650 }
3651
3652 /****************************************************************************
3653  *
3654  * Windows and scrolling tester.
3655  *
3656  ****************************************************************************/
3657
3658 #define BOTLINES        4       /* number of line stolen from screen bottom */
3659
3660 typedef struct {
3661     int y, x;
3662 } pair;
3663
3664 #define FRAME struct frame
3665 FRAME
3666 {
3667     FRAME *next, *last;
3668     bool do_scroll;
3669     bool do_keypad;
3670     WINDOW *wind;
3671 };
3672
3673 #if defined(NCURSES_VERSION)
3674 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
3675 #define is_keypad(win)   (win)->_use_keypad
3676 #define is_scrollok(win) (win)->_scroll
3677 #elif !defined(is_keypad)
3678 #define is_keypad(win)   FALSE
3679 #define is_scrollok(win) FALSE
3680 #endif
3681 #else
3682 #define is_keypad(win)   FALSE
3683 #define is_scrollok(win) FALSE
3684 #endif
3685
3686 static WINDOW *
3687 frame_win(FRAME * curp)
3688 {
3689     return (curp != 0) ? curp->wind : stdscr;
3690 }
3691
3692 /* We need to know if these flags are actually set, so don't look in FRAME.
3693  * These names are known to work with SVr4 curses as well as ncurses.  The
3694  * _use_keypad name does not work with Solaris 8.
3695  */
3696 static bool
3697 HaveKeypad(FRAME * curp)
3698 {
3699     WINDOW *win = frame_win(curp);
3700     (void) win;
3701     return is_keypad(win);
3702 }
3703
3704 static bool
3705 HaveScroll(FRAME * curp)
3706 {
3707     WINDOW *win = frame_win(curp);
3708     (void) win;
3709     return is_scrollok(win);
3710 }
3711
3712 static void
3713 newwin_legend(FRAME * curp)
3714 {
3715     static const struct {
3716         const char *msg;
3717         int code;
3718     } legend[] = {
3719         {
3720             "^C = create window", 0
3721         },
3722         {
3723             "^N = next window", 0
3724         },
3725         {
3726             "^P = previous window", 0
3727         },
3728         {
3729             "^F = scroll forward", 0
3730         },
3731         {
3732             "^B = scroll backward", 0
3733         },
3734         {
3735             "^K = keypad(%s)", 1
3736         },
3737         {
3738             "^S = scrollok(%s)", 2
3739         },
3740         {
3741             "^W = save window to file", 0
3742         },
3743         {
3744             "^R = restore window", 0
3745         },
3746 #if HAVE_WRESIZE
3747         {
3748             "^X = resize", 0
3749         },
3750 #endif
3751         {
3752             "^Q%s = exit", 3
3753         }
3754     };
3755     size_t n;
3756     int x;
3757     bool do_keypad = HaveKeypad(curp);
3758     bool do_scroll = HaveScroll(curp);
3759     char buf[BUFSIZ];
3760
3761     move(LINES - 4, 0);
3762     for (n = 0; n < SIZEOF(legend); n++) {
3763         switch (legend[n].code) {
3764         default:
3765             strcpy(buf, legend[n].msg);
3766             break;
3767         case 1:
3768             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3769             break;
3770         case 2:
3771             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3772             break;
3773         case 3:
3774             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3775             break;
3776         }
3777         x = getcurx(stdscr);
3778         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3779         addstr(buf);
3780     }
3781     clrtoeol();
3782 }
3783
3784 static void
3785 transient(FRAME * curp, NCURSES_CONST char *msg)
3786 {