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