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