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