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