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