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