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