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