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