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