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