]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
ncurses 5.7 - patch 20081206
[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.332 2008/11/29 20:08:42 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
52
53 #if HAVE_GETTIMEOFDAY
54 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #include <sys/time.h>
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #endif
61
62 #if USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 extern unsigned _nc_tracing;
81 #endif
82
83 #else
84
85 #define NCURSES_CONST_PARAM char
86
87 #define mmask_t chtype          /* not specified in XSI */
88
89 #ifndef ACS_S3
90 #ifdef CURSES_ACS_ARRAY
91 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
92 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
93 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
94 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
95 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
96 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
97 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
98 #else
99 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
100 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
101 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
102 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
103 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
104 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
105 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
106 #endif
107 #endif /* ACS_S3 */
108
109 #ifdef CURSES_WACS_ARRAY
110 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
111 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
112 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
113 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
114 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
115 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
116 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
117 #endif
118
119 #endif
120
121 #if HAVE_WCSRTOMBS
122 #define count_wchars(src, len, state)      wcsrtombs(0,   &src, len, state)
123 #define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
124 #define reset_wchars(state) memset(&state, 0, sizeof(state))
125 #elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
126 #define count_wchars(src, len, state)      wcstombs(0,   src, len)
127 #define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
128 #define reset_wchars(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
129 #define state_unused
130 #endif
131
132 #if HAVE_MBSRTOWCS
133 #define count_mbytes(src, len, state)      mbsrtowcs(0,   &src, len, state)
134 #define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
135 #define reset_mbytes(state) memset(&state, 0, sizeof(state))
136 #elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
137 #define count_mbytes(src, len, state)      mbstowcs(0,   src, len)
138 #define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
139 #define reset_mbytes(state) mblen(NULL, 0), mbtowc(NULL, NULL, 0)
140 #define state_unused
141 #endif
142
143 #define ToggleAcs(temp,real) temp = ((temp == real) ? 0 : real)
144
145 #define P(string)       printw("%s\n", string)
146
147 #define BLANK           ' '     /* this is the background character */
148
149 #undef max_colors
150 static int max_colors;          /* the actual number of colors we'll use */
151 static int min_colors;          /* the minimum color code */
152 static bool use_colors;         /* true if we use colors */
153
154 #undef max_pairs
155 static int max_pairs;           /* ...and the number of color pairs */
156
157 typedef struct {
158     short red;
159     short green;
160     short blue;
161 } RGB_DATA;
162
163 static RGB_DATA *all_colors;
164
165 static void main_menu(bool);
166
167 /* The behavior of mvhline, mvvline for negative/zero length is unspecified,
168  * though we can rely on negative x/y values to stop the macro.
169  */
170 static void
171 do_h_line(int y, int x, chtype c, int to)
172 {
173     if ((to) > (x))
174         mvhline(y, x, c, (to) - (x));
175 }
176
177 static void
178 do_v_line(int y, int x, chtype c, int to)
179 {
180     if ((to) > (y))
181         mvvline(y, x, c, (to) - (y));
182 }
183
184 static void
185 Repaint(void)
186 {
187     touchwin(stdscr);
188     touchwin(curscr);
189     wrefresh(curscr);
190 }
191
192 static bool
193 isQuit(int c)
194 {
195     return ((c) == QUIT || (c) == ESCAPE);
196 }
197 #define case_QUIT       QUIT: case ESCAPE
198
199 /* Common function to allow ^T to toggle trace-mode in the middle of a test
200  * so that trace-files can be made smaller.
201  */
202 static int
203 wGetchar(WINDOW *win)
204 {
205     int c;
206 #ifdef TRACE
207     while ((c = wgetch(win)) == CTRL('T')) {
208         if (_nc_tracing) {
209             save_trace = _nc_tracing;
210             Trace(("TOGGLE-TRACING OFF"));
211             _nc_tracing = 0;
212         } else {
213             _nc_tracing = save_trace;
214         }
215         trace(_nc_tracing);
216         if (_nc_tracing)
217             Trace(("TOGGLE-TRACING ON"));
218     }
219 #else
220     c = wgetch(win);
221 #endif
222     return c;
223 }
224 #define Getchar() wGetchar(stdscr)
225
226 /* replaces wgetnstr(), since we want to be able to edit values */
227 static void
228 wGetstring(WINDOW *win, char *buffer, int limit)
229 {
230     int y0, x0, x, ch;
231     bool done = FALSE;
232
233     echo();
234     getyx(win, y0, x0);
235     wattrset(win, A_REVERSE);
236
237     x = (int) strlen(buffer);
238     while (!done) {
239         if (x > (int) strlen(buffer))
240             x = (int) strlen(buffer);
241         wmove(win, y0, x0);
242         wprintw(win, "%-*s", limit, buffer);
243         wmove(win, y0, x0 + x);
244         switch (ch = wGetchar(win)) {
245         case '\n':
246         case KEY_ENTER:
247             done = TRUE;
248             break;
249         case CTRL('U'):
250             *buffer = '\0';
251             break;
252         case '\b':
253         case KEY_BACKSPACE:
254         case KEY_DC:
255             if (x > 0) {
256                 int j;
257                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
258                     ;
259                 }
260             } else {
261                 beep();
262             }
263             break;
264         case KEY_LEFT:
265             if (x > 0) {
266                 --x;
267             } else {
268                 flash();
269             }
270             break;
271         case KEY_RIGHT:
272             ++x;
273             break;
274         default:
275             if (!isprint(ch) || ch >= KEY_MIN) {
276                 beep();
277             } else if ((int) strlen(buffer) < limit) {
278                 int j;
279                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
280                     buffer[j] = buffer[j - 1];
281                 }
282                 buffer[x++] = (char) ch;
283             } else {
284                 flash();
285             }
286         }
287     }
288
289     wattroff(win, A_REVERSE);
290     wmove(win, y0, x0);
291     noecho();
292 }
293
294 #if USE_WIDEC_SUPPORT
295 static wchar_t
296 fullwidth_of(int ch)
297 {
298     return (ch + 0xff10 - '0');
299 }
300
301 static void
302 make_fullwidth_text(wchar_t *target, const char *source)
303 {
304     int ch;
305     while ((ch = *source++) != 0) {
306         *target++ = fullwidth_of(ch);
307     }
308     *target = 0;
309 }
310
311 static void
312 make_narrow_text(wchar_t *target, const char *source)
313 {
314     int ch;
315     while ((ch = *source++) != 0) {
316         *target++ = ch;
317     }
318     *target = 0;
319 }
320
321 static void
322 make_fullwidth_digit(cchar_t *target, int digit)
323 {
324     wchar_t source[2];
325
326     source[0] = fullwidth_of(digit + '0');
327     source[1] = 0;
328     setcchar(target, source, A_NORMAL, 0, 0);
329 }
330
331 static int
332 wGet_wchar(WINDOW *win, wint_t *result)
333 {
334     int c;
335 #ifdef TRACE
336     while ((c = wget_wch(win, result)) == CTRL('T')) {
337         if (_nc_tracing) {
338             save_trace = _nc_tracing;
339             Trace(("TOGGLE-TRACING OFF"));
340             _nc_tracing = 0;
341         } else {
342             _nc_tracing = save_trace;
343         }
344         trace(_nc_tracing);
345         if (_nc_tracing)
346             Trace(("TOGGLE-TRACING ON"));
347     }
348 #else
349     c = wget_wch(win, result);
350 #endif
351     return c;
352 }
353 #define Get_wchar(result) wGet_wchar(stdscr, result)
354
355 /* replaces wgetn_wstr(), since we want to be able to edit values */
356 static void
357 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
358 {
359     int y0, x0, x;
360     wint_t ch;
361     bool done = FALSE;
362     bool fkey = FALSE;
363
364     echo();
365     getyx(win, y0, x0);
366     wattrset(win, A_REVERSE);
367
368     x = (int) wcslen(buffer);
369     while (!done) {
370         if (x > (int) wcslen(buffer))
371             x = (int) wcslen(buffer);
372
373         /* clear the "window' */
374         wmove(win, y0, x0);
375         wprintw(win, "%*s", limit, " ");
376
377         /* write the existing buffer contents */
378         wmove(win, y0, x0);
379         waddnwstr(win, buffer, limit);
380
381         /* positions the cursor past character 'x' */
382         wmove(win, y0, x0);
383         waddnwstr(win, buffer, x);
384
385         switch (wGet_wchar(win, &ch)) {
386         case KEY_CODE_YES:
387             fkey = TRUE;
388             switch (ch) {
389             case KEY_ENTER:
390                 ch = '\n';
391                 fkey = FALSE;
392                 break;
393             case KEY_BACKSPACE:
394             case KEY_DC:
395                 ch = '\b';
396                 fkey = FALSE;
397                 break;
398             case KEY_LEFT:
399             case KEY_RIGHT:
400                 break;
401             default:
402                 ch = (wint_t) -1;
403                 break;
404             }
405             break;
406         case OK:
407             fkey = FALSE;
408             break;
409         default:
410             ch = (wint_t) -1;
411             fkey = TRUE;
412             break;
413         }
414
415         switch (ch) {
416         case '\n':
417             done = TRUE;
418             break;
419         case CTRL('U'):
420             *buffer = '\0';
421             break;
422         case '\b':
423             if (x > 0) {
424                 int j;
425                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
426                     ;
427                 }
428             } else {
429                 beep();
430             }
431             break;
432         case KEY_LEFT:
433             if (x > 0) {
434                 --x;
435             } else {
436                 beep();
437             }
438             break;
439         case KEY_RIGHT:
440             ++x;
441             break;
442         default:
443             if (fkey) {
444                 beep();
445             } else if ((int) wcslen(buffer) < limit) {
446                 int j;
447                 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
448                     buffer[j] = buffer[j - 1];
449                 }
450                 buffer[x++] = (wchar_t) ch;
451             } else {
452                 beep();
453             }
454         }
455     }
456
457     wattroff(win, A_REVERSE);
458     wmove(win, y0, x0);
459     noecho();
460 }
461
462 #endif
463
464 static void
465 Pause(void)
466 {
467     move(LINES - 1, 0);
468     addstr("Press any key to continue... ");
469     (void) Getchar();
470 }
471
472 static void
473 Cannot(const char *what)
474 {
475     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
476     Pause();
477 }
478
479 static void
480 ShellOut(bool message)
481 {
482     if (message)
483         addstr("Shelling out...");
484     def_prog_mode();
485     endwin();
486     system("sh");
487     if (message)
488         addstr("returned from shellout.\n");
489     refresh();
490 }
491
492 #ifdef NCURSES_MOUSE_VERSION
493 /*
494  * This function is the same as _tracemouse(), but we cannot count on that
495  * being available in the non-debug library.
496  */
497 static const char *
498 mouse_decode(MEVENT const *ep)
499 {
500     static char buf[80 + (5 * 10) + (32 * 15)];
501
502     (void) sprintf(buf, "id %2d  at (%2d, %2d, %2d) state %4lx = {",
503                    ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
504
505 #define SHOW(m, s) if ((ep->bstate & m)==m) {strcat(buf,s); strcat(buf, ", ");}
506
507     SHOW(BUTTON1_RELEASED, "release-1");
508     SHOW(BUTTON1_PRESSED, "press-1");
509     SHOW(BUTTON1_CLICKED, "click-1");
510     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
511     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
512 #if NCURSES_MOUSE_VERSION == 1
513     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
514 #endif
515
516     SHOW(BUTTON2_RELEASED, "release-2");
517     SHOW(BUTTON2_PRESSED, "press-2");
518     SHOW(BUTTON2_CLICKED, "click-2");
519     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
520     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
521 #if NCURSES_MOUSE_VERSION == 1
522     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
523 #endif
524
525     SHOW(BUTTON3_RELEASED, "release-3");
526     SHOW(BUTTON3_PRESSED, "press-3");
527     SHOW(BUTTON3_CLICKED, "click-3");
528     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
529     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
530 #if NCURSES_MOUSE_VERSION == 1
531     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
532 #endif
533
534     SHOW(BUTTON4_RELEASED, "release-4");
535     SHOW(BUTTON4_PRESSED, "press-4");
536     SHOW(BUTTON4_CLICKED, "click-4");
537     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
538     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
539 #if NCURSES_MOUSE_VERSION == 1
540     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
541 #endif
542
543 #if NCURSES_MOUSE_VERSION == 2
544     SHOW(BUTTON5_RELEASED, "release-5");
545     SHOW(BUTTON5_PRESSED, "press-5");
546     SHOW(BUTTON5_CLICKED, "click-5");
547     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
548     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
549 #endif
550
551     SHOW(BUTTON_CTRL, "ctrl");
552     SHOW(BUTTON_SHIFT, "shift");
553     SHOW(BUTTON_ALT, "alt");
554     SHOW(ALL_MOUSE_EVENTS, "all-events");
555     SHOW(REPORT_MOUSE_POSITION, "position");
556
557 #undef SHOW
558
559     if (buf[strlen(buf) - 1] == ' ')
560         buf[strlen(buf) - 2] = '\0';
561     (void) strcat(buf, "}");
562     return (buf);
563 }
564 #endif /* NCURSES_MOUSE_VERSION */
565
566 /****************************************************************************
567  *
568  * Character input test
569  *
570  ****************************************************************************/
571
572 #define NUM_GETCH_FLAGS 256
573 typedef bool GetchFlags[NUM_GETCH_FLAGS];
574
575 static void
576 setup_getch(WINDOW *win, GetchFlags flags)
577 {
578     keypad(win, flags['k']);    /* should be redundant, but for testing */
579     meta(win, flags['m']);      /* force this to a known state */
580     if (flags['e'])
581         echo();
582     else
583         noecho();
584 }
585
586 static void
587 init_getch(WINDOW *win, GetchFlags flags)
588 {
589     memset(flags, FALSE, NUM_GETCH_FLAGS);
590     flags[UChar('k')] = (win == stdscr);
591     flags[UChar('m')] = TRUE;
592
593     setup_getch(win, flags);
594 }
595
596 static void
597 wgetch_help(WINDOW *win, GetchFlags flags)
598 {
599     static const char *help[] =
600     {
601         "e  -- toggle echo mode"
602         ,"g  -- triggers a getstr test"
603         ,"k  -- toggle keypad/literal mode"
604         ,"m  -- toggle meta (7-bit/8-bit) mode"
605         ,"^q -- quit"
606         ,"s  -- shell out\n"
607         ,"w  -- create a new window"
608 #ifdef SIGTSTP
609         ,"z  -- suspend this process"
610 #endif
611     };
612     int y, x;
613     unsigned chk = ((SIZEOF(help) + 1) / 2);
614     unsigned n;
615
616     getyx(win, y, x);
617     move(0, 0);
618     printw("Type any key to see its %s value.  Also:\n",
619            flags['k'] ? "keypad" : "literal");
620     for (n = 0; n < SIZEOF(help); ++n) {
621         int row = 1 + (int) (n % chk);
622         int col = (n >= chk) ? COLS / 2 : 0;
623         int flg = ((strstr(help[n], "toggle") != 0)
624                    && (flags[UChar(*help[n])] != FALSE));
625         if (flg)
626             standout();
627         mvprintw(row, col, "%s", help[n]);
628         if (col == 0)
629             clrtoeol();
630         if (flg)
631             standend();
632     }
633     wrefresh(stdscr);
634     wmove(win, y, x);
635 }
636
637 static void
638 wgetch_wrap(WINDOW *win, int first_y)
639 {
640     int last_y = getmaxy(win) - 1;
641     int y = getcury(win) + 1;
642
643     if (y >= last_y)
644         y = first_y;
645     wmove(win, y, 0);
646     wclrtoeol(win);
647 }
648
649 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
650 typedef struct {
651     WINDOW *text;
652     WINDOW *frame;
653 } WINSTACK;
654
655 static WINSTACK *winstack = 0;
656 static unsigned len_winstack = 0;
657
658 static void
659 forget_boxes(void)
660 {
661     if (winstack != 0) {
662         free(winstack);
663     }
664     winstack = 0;
665     len_winstack = 0;
666 }
667
668 static void
669 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
670 {
671     unsigned need = (level + 1) * 2;
672
673     assert(level < COLS);
674
675     if (winstack == 0) {
676         len_winstack = 20;
677         winstack = typeMalloc(WINSTACK, len_winstack);
678     } else if (need >= len_winstack) {
679         len_winstack = need;
680         winstack = typeRealloc(WINSTACK, len_winstack, winstack);
681     }
682     winstack[level].text = txt_win;
683     winstack[level].frame = box_win;
684 }
685
686 #if USE_SOFTKEYS && (NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
687 static void
688 slk_repaint(void)
689 {
690     /* this chunk is now done in resize_term() */
691     slk_touch();
692     slk_clear();
693     slk_noutrefresh();
694 }
695
696 #else
697 #define slk_repaint()           /* nothing */
698 #endif
699
700 /*
701  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
702  * Resize both and paint the box in the parent.
703  */
704 static void
705 resize_boxes(unsigned level, WINDOW *win)
706 {
707     unsigned n;
708     int base = 5;
709     int high = LINES - base;
710     int wide = COLS;
711
712     touchwin(stdscr);
713     wnoutrefresh(stdscr);
714
715     slk_repaint();
716
717     for (n = 0; n < level; ++n) {
718         wresize(winstack[n].frame, high, wide);
719         wresize(winstack[n].text, high - 2, wide - 2);
720         high -= 2;
721         wide -= 2;
722         werase(winstack[n].text);
723         box(winstack[n].frame, 0, 0);
724         wnoutrefresh(winstack[n].frame);
725         wprintw(winstack[n].text,
726                 "size %dx%d\n",
727                 getmaxy(winstack[n].text),
728                 getmaxx(winstack[n].text));
729         wnoutrefresh(winstack[n].text);
730         if (winstack[n].text == win)
731             break;
732     }
733     doupdate();
734 }
735 #else
736 #define forget_boxes()          /* nothing */
737 #define remember_boxes(level,text,frame)        /* nothing */
738 #endif
739
740 static void
741 wgetch_test(unsigned level, WINDOW *win, int delay)
742 {
743     char buf[BUFSIZ];
744     int first_y, first_x;
745     int c;
746     int incount = 0;
747     GetchFlags flags;
748     bool blocking = (delay < 0);
749
750     init_getch(win, flags);
751     wtimeout(win, delay);
752     getyx(win, first_y, first_x);
753
754     wgetch_help(win, flags);
755     wsetscrreg(win, first_y, getmaxy(win) - 1);
756     scrollok(win, TRUE);
757
758     for (;;) {
759         while ((c = wGetchar(win)) == ERR) {
760             incount++;
761             if (blocking) {
762                 (void) wprintw(win, "%05d: input error", incount);
763                 break;
764             } else {
765                 (void) wprintw(win, "%05d: input timed out", incount);
766             }
767             wgetch_wrap(win, first_y);
768         }
769         if (c == ERR && blocking) {
770             wprintw(win, "ERR");
771             wgetch_wrap(win, first_y);
772         } else if (isQuit(c)) {
773             break;
774         } else if (c == 'e') {
775             flags[UChar('e')] = !flags[UChar('e')];
776             setup_getch(win, flags);
777             wgetch_help(win, flags);
778         } else if (c == 'g') {
779             waddstr(win, "getstr test: ");
780             echo();
781             wgetnstr(win, buf, sizeof(buf) - 1);
782             noecho();
783             wprintw(win, "I saw %d characters:\n\t`%s'.", (int) strlen(buf), buf);
784             wclrtoeol(win);
785             wgetch_wrap(win, first_y);
786         } else if (c == 'k') {
787             flags[UChar('k')] = !flags[UChar('k')];
788             setup_getch(win, flags);
789             wgetch_help(win, flags);
790         } else if (c == 'm') {
791             flags[UChar('m')] = !flags[UChar('m')];
792             setup_getch(win, flags);
793             wgetch_help(win, flags);
794         } else if (c == 's') {
795             ShellOut(TRUE);
796         } else if (c == 'w') {
797             int high = getmaxy(win) - 1 - first_y + 1;
798             int wide = getmaxx(win) - first_x;
799             int old_y, old_x;
800             int new_y = first_y + getbegy(win);
801             int new_x = first_x + getbegx(win);
802
803             getyx(win, old_y, old_x);
804             if (high > 2 && wide > 2) {
805                 WINDOW *wb = newwin(high, wide, new_y, new_x);
806                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
807
808                 box(wb, 0, 0);
809                 wrefresh(wb);
810                 wmove(wi, 0, 0);
811                 remember_boxes(level, wi, wb);
812                 wgetch_test(level + 1, wi, delay);
813                 delwin(wi);
814                 delwin(wb);
815
816                 wgetch_help(win, flags);
817                 wmove(win, old_y, old_x);
818                 touchwin(win);
819                 wrefresh(win);
820                 doupdate();
821             }
822 #ifdef SIGTSTP
823         } else if (c == 'z') {
824             kill(getpid(), SIGTSTP);
825 #endif
826         } else {
827             wprintw(win, "Key pressed: %04o ", c);
828 #ifdef NCURSES_MOUSE_VERSION
829             if (c == KEY_MOUSE) {
830                 int y, x;
831                 MEVENT event;
832
833                 getmouse(&event);
834                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
835                 getyx(win, y, x);
836                 move(event.y, event.x);
837                 addch('*');
838                 wmove(win, y, x);
839             } else
840 #endif /* NCURSES_MOUSE_VERSION */
841             if (c >= KEY_MIN) {
842 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
843                 if (c == KEY_RESIZE) {
844                     resize_boxes(level, win);
845                 }
846 #endif
847                 (void) waddstr(win, keyname(c));
848             } else if (c >= 0x80) {
849                 unsigned c2 = (unsigned) c;
850 #if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
851                 /* at least Solaris SVR4 curses breaks unctrl(128), etc. */
852                 c2 &= 0x7f;
853 #endif
854                 if (isprint(c))
855                     (void) wprintw(win, "%c", UChar(c));
856                 else if (c2 != UChar(c))
857                     (void) wprintw(win, "M-%s", unctrl(c2));
858                 else
859                     (void) wprintw(win, "%s", unctrl(c2));
860                 waddstr(win, " (high-half character)");
861             } else {
862                 if (isprint(c))
863                     (void) wprintw(win, "%c (ASCII printable character)", c);
864                 else
865                     (void) wprintw(win, "%s (ASCII control character)",
866                                    unctrl(UChar(c)));
867             }
868             wgetch_wrap(win, first_y);
869         }
870     }
871
872     wtimeout(win, -1);
873
874     if (!level)
875         init_getch(win, flags);
876 }
877
878 static int
879 begin_getch_test(void)
880 {
881     char buf[BUFSIZ];
882     int delay;
883
884     refresh();
885
886 #ifdef NCURSES_MOUSE_VERSION
887     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
888 #endif
889
890     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
891     echo();
892     getnstr(buf, sizeof(buf) - 1);
893     noecho();
894     nonl();
895
896     if (isdigit(UChar(buf[0]))) {
897         delay = atoi(buf) * 100;
898     } else {
899         delay = -1;
900     }
901     raw();
902     move(5, 0);
903     return delay;
904 }
905
906 static void
907 finish_getch_test(void)
908 {
909 #ifdef NCURSES_MOUSE_VERSION
910     mousemask(0, (mmask_t *) 0);
911 #endif
912     erase();
913     noraw();
914     nl();
915     endwin();
916 }
917
918 static void
919 getch_test(void)
920 {
921     int delay = begin_getch_test();
922
923     slk_restore();
924     wgetch_test(0, stdscr, delay);
925     forget_boxes();
926     finish_getch_test();
927 }
928
929 #if USE_WIDEC_SUPPORT
930 /*
931  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
932  * Resize both and paint the box in the parent.
933  */
934 #if defined(KEY_RESIZE) && HAVE_WRESIZE
935 static void
936 resize_wide_boxes(unsigned level, WINDOW *win)
937 {
938     unsigned n;
939     int base = 5;
940     int high = LINES - base;
941     int wide = COLS;
942
943     touchwin(stdscr);
944     wnoutrefresh(stdscr);
945
946     slk_repaint();
947
948     for (n = 0; n < level; ++n) {
949         wresize(winstack[n].frame, high, wide);
950         wresize(winstack[n].text, high - 2, wide - 2);
951         high -= 2;
952         wide -= 2;
953         werase(winstack[n].text);
954         box_set(winstack[n].frame, 0, 0);
955         wnoutrefresh(winstack[n].frame);
956         wprintw(winstack[n].text,
957                 "size %dx%d\n",
958                 getmaxy(winstack[n].text),
959                 getmaxx(winstack[n].text));
960         wnoutrefresh(winstack[n].text);
961         if (winstack[n].text == win)
962             break;
963     }
964     doupdate();
965 }
966 #endif /* KEY_RESIZE */
967
968 static char *
969 wcstos(const wchar_t *src)
970 {
971     int need;
972     char *result = 0;
973     const wchar_t *tmp = src;
974 #ifndef state_unused
975     mbstate_t state;
976 #endif
977
978     reset_wchars(state);
979     if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
980         unsigned have = (unsigned) need;
981         if ((result = typeCalloc(char, have + 1)) != 0) {
982             tmp = src;
983             if (trans_wchars(result, tmp, have, &state) != have) {
984                 free(result);
985                 result = 0;
986             }
987         }
988     }
989     return result;
990 }
991
992 static void
993 wget_wch_test(unsigned level, WINDOW *win, int delay)
994 {
995     wchar_t wchar_buf[BUFSIZ];
996     wint_t wint_buf[BUFSIZ];
997     int first_y, first_x;
998     wint_t c;
999     int incount = 0;
1000     GetchFlags flags;
1001     bool blocking = (delay < 0);
1002     int y, x, code;
1003     char *temp;
1004
1005     init_getch(win, flags);
1006     wtimeout(win, delay);
1007     getyx(win, first_y, first_x);
1008
1009     wgetch_help(win, flags);
1010     wsetscrreg(win, first_y, getmaxy(win) - 1);
1011     scrollok(win, TRUE);
1012
1013     for (;;) {
1014         while ((code = wGet_wchar(win, &c)) == ERR) {
1015             incount++;
1016             if (blocking) {
1017                 (void) wprintw(win, "%05d: input error", incount);
1018                 break;
1019             } else {
1020                 (void) wprintw(win, "%05d: input timed out", incount);
1021             }
1022             wgetch_wrap(win, first_y);
1023         }
1024         if (code == ERR && blocking) {
1025             wprintw(win, "ERR");
1026             wgetch_wrap(win, first_y);
1027         } else if (isQuit((int) c)) {
1028             break;
1029         } else if (c == 'e') {
1030             flags[UChar('e')] = !flags[UChar('e')];
1031             setup_getch(win, flags);
1032             wgetch_help(win, flags);
1033         } else if (c == 'g') {
1034             waddstr(win, "getstr test: ");
1035             echo();
1036             code = wgetn_wstr(win, wint_buf, sizeof(wint_buf) - 1);
1037             noecho();
1038             if (code == ERR) {
1039                 wprintw(win, "wgetn_wstr returns an error.");
1040             } else {
1041                 int n;
1042                 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1043                     ;
1044                 }
1045                 if ((temp = wcstos(wchar_buf)) != 0) {
1046                     wprintw(win, "I saw %d characters:\n\t`%s'.",
1047                             (int) wcslen(wchar_buf), temp);
1048                     free(temp);
1049                 } else {
1050                     wprintw(win, "I saw %d characters (cannot convert).",
1051                             (int) wcslen(wchar_buf));
1052                 }
1053             }
1054             wclrtoeol(win);
1055             wgetch_wrap(win, first_y);
1056         } else if (c == 'k') {
1057             flags[UChar('k')] = !flags[UChar('k')];
1058             setup_getch(win, flags);
1059             wgetch_help(win, flags);
1060         } else if (c == 'm') {
1061             flags[UChar('m')] = !flags[UChar('m')];
1062             setup_getch(win, flags);
1063             wgetch_help(win, flags);
1064         } else if (c == 's') {
1065             ShellOut(TRUE);
1066         } else if (c == 'w') {
1067             int high = getmaxy(win) - 1 - first_y + 1;
1068             int wide = getmaxx(win) - first_x;
1069             int old_y, old_x;
1070             int new_y = first_y + getbegy(win);
1071             int new_x = first_x + getbegx(win);
1072
1073             getyx(win, old_y, old_x);
1074             if (high > 2 && wide > 2) {
1075                 WINDOW *wb = newwin(high, wide, new_y, new_x);
1076                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1077
1078                 box_set(wb, 0, 0);
1079                 wrefresh(wb);
1080                 wmove(wi, 0, 0);
1081                 remember_boxes(level, wi, wb);
1082                 wget_wch_test(level + 1, wi, delay);
1083                 delwin(wi);
1084                 delwin(wb);
1085
1086                 wgetch_help(win, flags);
1087                 wmove(win, old_y, old_x);
1088                 touchwin(win);
1089                 wrefresh(win);
1090             }
1091 #ifdef SIGTSTP
1092         } else if (c == 'z') {
1093             kill(getpid(), SIGTSTP);
1094 #endif
1095         } else {
1096             wprintw(win, "Key pressed: %04o ", (int) c);
1097 #ifdef NCURSES_MOUSE_VERSION
1098             if (c == KEY_MOUSE) {
1099                 MEVENT event;
1100
1101                 getmouse(&event);
1102                 wprintw(win, "KEY_MOUSE, %s", mouse_decode(&event));
1103                 getyx(win, y, x);
1104                 move(event.y, event.x);
1105                 addch('*');
1106                 wmove(win, y, x);
1107             } else
1108 #endif /* NCURSES_MOUSE_VERSION */
1109             if (code == KEY_CODE_YES) {
1110 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1111                 if (c == KEY_RESIZE) {
1112                     resize_wide_boxes(level, win);
1113                 }
1114 #endif
1115                 (void) waddstr(win, keyname((wchar_t) c));
1116             } else {
1117                 (void) waddstr(win, key_name((wchar_t) c));
1118                 if (c < 256 && iscntrl(c)) {
1119                     (void) wprintw(win, " (control character)");
1120                 } else {
1121                     (void) wprintw(win, " = %#x (printable character)",
1122                                    (unsigned) c);
1123                 }
1124             }
1125             wgetch_wrap(win, first_y);
1126         }
1127     }
1128
1129     wtimeout(win, -1);
1130
1131     if (!level)
1132         init_getch(win, flags);
1133 }
1134
1135 static void
1136 get_wch_test(void)
1137 {
1138     int delay = begin_getch_test();
1139
1140     slk_restore();
1141     wget_wch_test(0, stdscr, delay);
1142     forget_boxes();
1143     finish_getch_test();
1144 }
1145 #endif
1146
1147 /****************************************************************************
1148  *
1149  * Character attributes test
1150  *
1151  ****************************************************************************/
1152
1153 #if HAVE_SETUPTERM || HAVE_TGETENT
1154 #define get_ncv() TIGETNUM("ncv","NC")
1155 #define get_xmc() TIGETNUM("xmc","sg")
1156 #else
1157 #define get_ncv() -1
1158 #define get_xmc() -1
1159 #endif
1160
1161 #if !HAVE_TERMATTRS
1162 static chtype
1163 my_termattrs(void)
1164 {
1165     static int first = TRUE;
1166     static chtype result = 0;
1167
1168     if (first) {
1169 #if !HAVE_TIGETSTR
1170         char buffer[4096];
1171         char parsed[4096];
1172         char *area_pointer = parsed;
1173
1174         tgetent(buffer, getenv("TERM"));
1175 #endif
1176
1177         if (TIGETSTR("smso", "so"))
1178             result |= A_STANDOUT;
1179         if (TIGETSTR("smul", "us"))
1180             result |= A_UNDERLINE;
1181         if (TIGETSTR("rev", "mr"))
1182             result |= A_REVERSE;
1183         if (TIGETSTR("blink", "mb"))
1184             result |= A_BLINK;
1185         if (TIGETSTR("dim", "mh"))
1186             result |= A_DIM;
1187         if (TIGETSTR("bold", "md"))
1188             result |= A_BOLD;
1189         if (TIGETSTR("smacs", "ac"))
1190             result |= A_ALTCHARSET;
1191
1192         first = FALSE;
1193     }
1194     return result;
1195 }
1196 #define termattrs() my_termattrs()
1197 #endif
1198
1199 #define MAX_ATTRSTRING 31
1200 #define LEN_ATTRSTRING 26
1201
1202 static char attr_test_string[MAX_ATTRSTRING + 1];
1203
1204 static void
1205 attr_legend(WINDOW *helpwin)
1206 {
1207     int row = 1;
1208     int col = 1;
1209
1210     mvwprintw(helpwin, row++, col,
1211               "ESC to exit.");
1212     mvwprintw(helpwin, row++, col,
1213               "^L repaints.");
1214     ++row;
1215     mvwprintw(helpwin, row++, col,
1216               "Modify the test strings:");
1217     mvwprintw(helpwin, row++, col,
1218               "  A digit sets gaps on each side of displayed attributes");
1219     mvwprintw(helpwin, row++, col,
1220               "  </> shifts the text left/right. ");
1221     ++row;
1222     mvwprintw(helpwin, row++, col,
1223               "Toggles:");
1224     if (use_colors) {
1225         mvwprintw(helpwin, row++, col,
1226                   "  f/F/b/F toggle foreground/background background color");
1227         mvwprintw(helpwin, row++, col,
1228                   "  t/T     toggle text/background color attribute");
1229     }
1230     mvwprintw(helpwin, row++, col,
1231               "  a/A     toggle ACS (alternate character set) mapping");
1232     mvwprintw(helpwin, row++, col,
1233               "  v/V     toggle video attribute to combine with each line");
1234 }
1235
1236 static void
1237 show_color_attr(int fg, int bg, int tx)
1238 {
1239     if (use_colors) {
1240         printw("  Colors (fg %d, bg %d", fg, bg);
1241         if (tx >= 0)
1242             printw(", text %d", tx);
1243         printw("),");
1244     }
1245 }
1246
1247 static bool
1248 cycle_color_attr(int ch, short *fg, short *bg, short *tx)
1249 {
1250     bool error = FALSE;
1251
1252     if (use_colors) {
1253         switch (ch) {
1254         case 'f':
1255             *fg = (short) (*fg + 1);
1256             break;
1257         case 'F':
1258             *fg = (short) (*fg - 1);
1259             break;
1260         case 'b':
1261             *bg = (short) (*bg + 1);
1262             break;
1263         case 'B':
1264             *bg = (short) (*bg - 1);
1265             break;
1266         case 't':
1267             *tx = (short) (*tx + 1);
1268             break;
1269         case 'T':
1270             *tx = (short) (*tx - 1);
1271             break;
1272         default:
1273             beep();
1274             error = TRUE;
1275             break;
1276         }
1277         if (*fg >= COLORS)
1278             *fg = (short) min_colors;
1279         if (*fg < min_colors)
1280             *fg = (short) (COLORS - 1);
1281         if (*bg >= COLORS)
1282             *bg = (short) min_colors;
1283         if (*bg < min_colors)
1284             *bg = (short) (COLORS - 1);
1285         if (*tx >= COLORS)
1286             *tx = -1;
1287         if (*tx < -1)
1288             *tx = (short) (COLORS - 1);
1289     } else {
1290         beep();
1291         error = TRUE;
1292     }
1293     return error;
1294 }
1295
1296 static void
1297 adjust_attr_string(int adjust)
1298 {
1299     int first = ((int) UChar(attr_test_string[0])) + adjust;
1300     int last = first + LEN_ATTRSTRING;
1301
1302     if (first >= ' ' && last <= '~') {  /* 32..126 */
1303         int j, k;
1304         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1305             attr_test_string[j] = (char) k;
1306             if (((k + 1 - first) % 5) == 0) {
1307                 if (++j >= MAX_ATTRSTRING)
1308                     break;
1309                 attr_test_string[j] = ' ';
1310             }
1311         }
1312         while (j < MAX_ATTRSTRING)
1313             attr_test_string[j++] = ' ';
1314         attr_test_string[j] = '\0';
1315     } else {
1316         beep();
1317     }
1318 }
1319
1320 static void
1321 init_attr_string(void)
1322 {
1323     attr_test_string[0] = 'a';
1324     adjust_attr_string(0);
1325 }
1326
1327 static int
1328 show_attr(int row, int skip, bool arrow, chtype attr, const char *name)
1329 {
1330     int ncv = get_ncv();
1331     chtype test = attr & (chtype) (~A_ALTCHARSET);
1332
1333     if (arrow)
1334         mvprintw(row, 5, "-->");
1335     mvprintw(row, 8, "%s mode:", name);
1336     mvprintw(row, 24, "|");
1337     if (skip)
1338         printw("%*s", skip, " ");
1339     /*
1340      * Just for testing, write text using the alternate character set one
1341      * character at a time (to pass its rendition directly), and use the
1342      * string operation for the other attributes.
1343      */
1344     if (attr & A_ALTCHARSET) {
1345         const char *s;
1346         chtype ch;
1347
1348         for (s = attr_test_string; *s != '\0'; ++s) {
1349             ch = UChar(*s);
1350             addch(ch | attr);
1351         }
1352     } else {
1353         attrset(attr);
1354         addstr(attr_test_string);
1355         attroff(attr);
1356     }
1357     if (skip)
1358         printw("%*s", skip, " ");
1359     printw("|");
1360     if (test != A_NORMAL) {
1361         if (!(termattrs() & test)) {
1362             printw(" (N/A)");
1363         } else {
1364             if (ncv > 0 && (getbkgd(stdscr) & A_COLOR)) {
1365                 static const chtype table[] =
1366                 {
1367                     A_STANDOUT,
1368                     A_UNDERLINE,
1369                     A_REVERSE,
1370                     A_BLINK,
1371                     A_DIM,
1372                     A_BOLD,
1373 #ifdef A_INVIS
1374                     A_INVIS,
1375 #endif
1376                     A_PROTECT,
1377                     A_ALTCHARSET
1378                 };
1379                 unsigned n;
1380                 bool found = FALSE;
1381                 for (n = 0; n < SIZEOF(table); n++) {
1382                     if ((table[n] & attr) != 0
1383                         && ((1 << n) & ncv) != 0) {
1384                         found = TRUE;
1385                         break;
1386                     }
1387                 }
1388                 if (found)
1389                     printw(" (NCV)");
1390             }
1391             if ((termattrs() & test) != test)
1392                 printw(" (Part)");
1393         }
1394     }
1395     return row + 2;
1396 }
1397 /* *INDENT-OFF* */
1398 static const struct {
1399     chtype                      attr;
1400     NCURSES_CONST char *        name;
1401 } attrs_to_test[] = {
1402     { A_STANDOUT,       "STANDOUT" },
1403     { A_REVERSE,        "REVERSE" },
1404     { A_BOLD,           "BOLD" },
1405     { A_UNDERLINE,      "UNDERLINE" },
1406     { A_DIM,            "DIM" },
1407     { A_BLINK,          "BLINK" },
1408     { A_PROTECT,        "PROTECT" },
1409 #ifdef A_INVIS
1410     { A_INVIS,          "INVISIBLE" },
1411 #endif
1412     { A_NORMAL,         "NORMAL" },
1413 };
1414 /* *INDENT-ON* */
1415
1416 static bool
1417 attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1418 {
1419     bool result = TRUE;
1420     bool error = FALSE;
1421     WINDOW *helpwin;
1422
1423     do {
1424         int ch = Getchar();
1425
1426         error = FALSE;
1427         if (ch < 256 && isdigit(ch)) {
1428             *skip = (ch - '0');
1429         } else {
1430             switch (ch) {
1431             case CTRL('L'):
1432                 Repaint();
1433                 break;
1434             case '?':
1435                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1436                     box(helpwin, 0, 0);
1437                     attr_legend(helpwin);
1438                     wGetchar(helpwin);
1439                     delwin(helpwin);
1440                 }
1441                 break;
1442             case 'a':
1443                 *ac = 0;
1444                 break;
1445             case 'A':
1446                 *ac = A_ALTCHARSET;
1447                 break;
1448             case 'v':
1449                 if (*kc == 0)
1450                     *kc = SIZEOF(attrs_to_test) - 1;
1451                 else
1452                     *kc -= 1;
1453                 break;
1454             case 'V':
1455                 *kc += 1;
1456                 if (*kc >= SIZEOF(attrs_to_test))
1457                     *kc = 0;
1458                 break;
1459             case '<':
1460                 adjust_attr_string(-1);
1461                 break;
1462             case '>':
1463                 adjust_attr_string(1);
1464                 break;
1465             case case_QUIT:
1466                 result = FALSE;
1467                 break;
1468             default:
1469                 error = cycle_color_attr(ch, fg, bg, tx);
1470                 break;
1471             }
1472         }
1473     } while (error);
1474     return result;
1475 }
1476
1477 static void
1478 attr_test(void)
1479 /* test text attributes */
1480 {
1481     int n;
1482     int skip = get_xmc();
1483     short fg = COLOR_BLACK;     /* color pair 0 is special */
1484     short bg = COLOR_BLACK;
1485     short tx = -1;
1486     int ac = 0;
1487     unsigned j, k;
1488
1489     if (skip < 0)
1490         skip = 0;
1491
1492     n = skip;                   /* make it easy */
1493     k = SIZEOF(attrs_to_test) - 1;
1494     init_attr_string();
1495
1496     do {
1497         int row = 2;
1498         chtype normal = A_NORMAL | BLANK;
1499         chtype extras = (chtype) ac;
1500
1501         if (use_colors) {
1502             short pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1503             if (pair != 0) {
1504                 pair = 1;
1505                 if (init_pair(pair, fg, bg) == ERR) {
1506                     beep();
1507                 } else {
1508                     normal |= COLOR_PAIR(pair);
1509                 }
1510             }
1511             if (tx >= 0) {
1512                 pair = 2;
1513                 if (init_pair(pair, tx, bg) == ERR) {
1514                     beep();
1515                 } else {
1516                     extras |= COLOR_PAIR(pair);
1517                 }
1518             }
1519         }
1520         bkgd(normal);
1521         bkgdset(normal);
1522         erase();
1523
1524         box(stdscr, 0, 0);
1525         mvaddstr(0, 20, "Character attribute test display");
1526
1527         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1528             bool arrow = (j == k);
1529             row = show_attr(row, n, arrow,
1530                             extras |
1531                             attrs_to_test[j].attr |
1532                             attrs_to_test[k].attr,
1533                             attrs_to_test[j].name);
1534         }
1535
1536         mvprintw(row, 8,
1537                  "This terminal does %shave the magic-cookie glitch",
1538                  get_xmc() > -1 ? "" : "not ");
1539         mvprintw(row + 1, 8, "Enter '?' for help.");
1540         show_color_attr(fg, bg, tx);
1541         printw("  ACS (%d)", ac != 0);
1542
1543         refresh();
1544     } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1545
1546     bkgdset(A_NORMAL | BLANK);
1547     erase();
1548     endwin();
1549 }
1550
1551 #if USE_WIDEC_SUPPORT
1552 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1553
1554 static void
1555 wide_adjust_attr_string(int adjust)
1556 {
1557     int first = ((int) UChar(wide_attr_test_string[0])) + adjust;
1558     int last = first + LEN_ATTRSTRING;
1559
1560     if (first >= ' ' && last <= '~') {  /* 32..126 */
1561         int j, k;
1562         for (j = 0, k = first; j < MAX_ATTRSTRING && k <= last; ++j, ++k) {
1563             wide_attr_test_string[j] = k;
1564             if (((k + 1 - first) % 5) == 0) {
1565                 if (++j >= MAX_ATTRSTRING)
1566                     break;
1567                 wide_attr_test_string[j] = ' ';
1568             }
1569         }
1570         while (j < MAX_ATTRSTRING)
1571             wide_attr_test_string[j++] = ' ';
1572         wide_attr_test_string[j] = '\0';
1573     } else {
1574         beep();
1575     }
1576 }
1577
1578 static void
1579 wide_init_attr_string(void)
1580 {
1581     wide_attr_test_string[0] = 'a';
1582     wide_adjust_attr_string(0);
1583 }
1584
1585 static void
1586 set_wide_background(short pair)
1587 {
1588     cchar_t normal;
1589     wchar_t blank[2];
1590
1591     blank[0] = ' ';
1592     blank[1] = 0;
1593     setcchar(&normal, blank, A_NORMAL, pair, 0);
1594     bkgrnd(&normal);
1595     bkgrndset(&normal);
1596 }
1597
1598 static attr_t
1599 get_wide_background(void)
1600 {
1601     attr_t result = A_NORMAL;
1602     attr_t attr;
1603     cchar_t ch;
1604     short pair;
1605     wchar_t wch[10];
1606
1607     if (getbkgrnd(&ch) != ERR) {
1608         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1609             result = attr;
1610         }
1611     }
1612     return result;
1613 }
1614
1615 static int
1616 wide_show_attr(int row, int skip, bool arrow, chtype attr, short pair, const char *name)
1617 {
1618     int ncv = get_ncv();
1619     chtype test = attr & ~WA_ALTCHARSET;
1620
1621     if (arrow)
1622         mvprintw(row, 5, "-->");
1623     mvprintw(row, 8, "%s mode:", name);
1624     mvprintw(row, 24, "|");
1625     if (skip)
1626         printw("%*s", skip, " ");
1627
1628     /*
1629      * Just for testing, write text using the alternate character set one
1630      * character at a time (to pass its rendition directly), and use the
1631      * string operation for the other attributes.
1632      */
1633     if (attr & WA_ALTCHARSET) {
1634         const wchar_t *s;
1635         cchar_t ch;
1636
1637         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1638             wchar_t fill[2];
1639             fill[0] = *s;
1640             fill[1] = L'\0';
1641             setcchar(&ch, fill, attr, pair, 0);
1642             add_wch(&ch);
1643         }
1644     } else {
1645         attr_t old_attr;
1646         short old_pair;
1647
1648         attr_get(&old_attr, &old_pair, 0);
1649         attr_set(attr, pair, 0);
1650         addwstr(wide_attr_test_string);
1651         attr_set(old_attr, old_pair, 0);
1652     }
1653     if (skip)
1654         printw("%*s", skip, " ");
1655     printw("|");
1656     if (test != A_NORMAL) {
1657         if (!(term_attrs() & test)) {
1658             printw(" (N/A)");
1659         } else {
1660             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1661                 static const attr_t table[] =
1662                 {
1663                     WA_STANDOUT,
1664                     WA_UNDERLINE,
1665                     WA_REVERSE,
1666                     WA_BLINK,
1667                     WA_DIM,
1668                     WA_BOLD,
1669                     WA_INVIS,
1670                     WA_PROTECT,
1671                     WA_ALTCHARSET
1672                 };
1673                 unsigned n;
1674                 bool found = FALSE;
1675                 for (n = 0; n < SIZEOF(table); n++) {
1676                     if ((table[n] & attr) != 0
1677                         && ((1 << n) & ncv) != 0) {
1678                         found = TRUE;
1679                         break;
1680                     }
1681                 }
1682                 if (found)
1683                     printw(" (NCV)");
1684             }
1685             if ((term_attrs() & test) != test)
1686                 printw(" (Part)");
1687         }
1688     }
1689     return row + 2;
1690 }
1691
1692 static bool
1693 wide_attr_getc(int *skip, short *fg, short *bg, short *tx, int *ac, unsigned *kc)
1694 {
1695     bool result = TRUE;
1696     bool error = FALSE;
1697     WINDOW *helpwin;
1698
1699     do {
1700         int ch = Getchar();
1701
1702         error = FALSE;
1703         if (ch < 256 && isdigit(ch)) {
1704             *skip = (ch - '0');
1705         } else {
1706             switch (ch) {
1707             case CTRL('L'):
1708                 Repaint();
1709                 break;
1710             case '?':
1711                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1712                     box_set(helpwin, 0, 0);
1713                     attr_legend(helpwin);
1714                     wGetchar(helpwin);
1715                     delwin(helpwin);
1716                 }
1717                 break;
1718             case 'a':
1719                 *ac = 0;
1720                 break;
1721             case 'A':
1722                 *ac = A_ALTCHARSET;
1723                 break;
1724             case 'v':
1725                 if (*kc == 0)
1726                     *kc = SIZEOF(attrs_to_test) - 1;
1727                 else
1728                     *kc -= 1;
1729                 break;
1730             case 'V':
1731                 *kc += 1;
1732                 if (*kc >= SIZEOF(attrs_to_test))
1733                     *kc = 0;
1734                 break;
1735             case '<':
1736                 wide_adjust_attr_string(-1);
1737                 break;
1738             case '>':
1739                 wide_adjust_attr_string(1);
1740                 break;
1741             case case_QUIT:
1742                 result = FALSE;
1743                 break;
1744             default:
1745                 error = cycle_color_attr(ch, fg, bg, tx);
1746                 break;
1747             }
1748         }
1749     } while (error);
1750     return result;
1751 }
1752
1753 static void
1754 wide_attr_test(void)
1755 /* test text attributes using wide-character calls */
1756 {
1757     int n;
1758     int skip = get_xmc();
1759     short fg = COLOR_BLACK;     /* color pair 0 is special */
1760     short bg = COLOR_BLACK;
1761     short tx = -1;
1762     int ac = 0;
1763     unsigned j, k;
1764
1765     if (skip < 0)
1766         skip = 0;
1767
1768     n = skip;                   /* make it easy */
1769     k = SIZEOF(attrs_to_test) - 1;
1770     wide_init_attr_string();
1771
1772     do {
1773         int row = 2;
1774         short pair = 0;
1775         short extras = 0;
1776
1777         if (use_colors) {
1778             pair = (short) (fg != COLOR_BLACK || bg != COLOR_BLACK);
1779             if (pair != 0) {
1780                 pair = 1;
1781                 if (init_pair(pair, fg, bg) == ERR) {
1782                     beep();
1783                 }
1784             }
1785             extras = pair;
1786             if (tx >= 0) {
1787                 extras = 2;
1788                 if (init_pair(extras, tx, bg) == ERR) {
1789                     beep();
1790                 }
1791             }
1792         }
1793         set_wide_background(pair);
1794         erase();
1795
1796         box_set(stdscr, 0, 0);
1797         mvaddstr(0, 20, "Character attribute test display");
1798
1799         for (j = 0; j < SIZEOF(attrs_to_test); ++j) {
1800             row = wide_show_attr(row, n, j == k,
1801                                  ac |
1802                                  attrs_to_test[j].attr |
1803                                  attrs_to_test[k].attr,
1804                                  extras,
1805                                  attrs_to_test[j].name);
1806         }
1807
1808         mvprintw(row, 8,
1809                  "This terminal does %shave the magic-cookie glitch",
1810                  get_xmc() > -1 ? "" : "not ");
1811         mvprintw(row + 1, 8, "Enter '?' for help.");
1812         show_color_attr(fg, bg, tx);
1813         printw("  ACS (%d)", ac != 0);
1814
1815         refresh();
1816     } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k));
1817
1818     set_wide_background(0);
1819     erase();
1820     endwin();
1821 }
1822 #endif
1823
1824 /****************************************************************************
1825  *
1826  * Color support tests
1827  *
1828  ****************************************************************************/
1829
1830 static NCURSES_CONST char *the_color_names[] =
1831 {
1832     "black",
1833     "red",
1834     "green",
1835     "yellow",
1836     "blue",
1837     "magenta",
1838     "cyan",
1839     "white",
1840     "BLACK",
1841     "RED",
1842     "GREEN",
1843     "YELLOW",
1844     "BLUE",
1845     "MAGENTA",
1846     "CYAN",
1847     "WHITE"
1848 };
1849
1850 static void
1851 show_color_name(int y, int x, int color, bool wide)
1852 {
1853     if (move(y, x) != ERR) {
1854         char temp[80];
1855         int width = 8;
1856
1857         if (wide) {
1858             sprintf(temp, "%02d", color);
1859             width = 4;
1860         } else if (color >= 8) {
1861             sprintf(temp, "[%02d]", color);
1862         } else {
1863             strcpy(temp, the_color_names[color]);
1864         }
1865         printw("%-*.*s", width, width, temp);
1866     }
1867 }
1868
1869 static void
1870 color_legend(WINDOW *helpwin, bool wide)
1871 {
1872     int row = 1;
1873     int col = 1;
1874
1875     mvwprintw(helpwin, row++, col,
1876               "ESC to exit.");
1877     ++row;
1878     mvwprintw(helpwin, row++, col,
1879               "Use up/down arrow to scroll through the display if it is");
1880     mvwprintw(helpwin, row++, col,
1881               "longer than one screen. Control/N and Control/P can be used");
1882     mvwprintw(helpwin, row++, col,
1883               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
1884     mvwprintw(helpwin, row++, col,
1885               "full screen; control/B and control/F can be used here.");
1886     ++row;
1887     mvwprintw(helpwin, row++, col,
1888               "Toggles:");
1889     mvwprintw(helpwin, row++, col,
1890               "  a/A     toggle altcharset off/on");
1891     mvwprintw(helpwin, row++, col,
1892               "  b/B     toggle bold off/on");
1893     mvwprintw(helpwin, row++, col,
1894               "  n/N     toggle text/number on/off");
1895     mvwprintw(helpwin, row++, col,
1896               "  w/W     toggle width between 8/16 colors");
1897 #if USE_WIDEC_SUPPORT
1898     if (wide) {
1899         mvwprintw(helpwin, row++, col,
1900                   "Wide characters:");
1901         mvwprintw(helpwin, row++, col,
1902                   "  x/X     toggle text between ASCII and wide-character");
1903     }
1904 #else
1905     (void) wide;
1906 #endif
1907 }
1908
1909 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1910
1911 /* generate a color test pattern */
1912 static void
1913 color_test(void)
1914 {
1915     short i;
1916     int top = 0, width;
1917     int base_row = 0;
1918     int grid_top = top + 3;
1919     int page_size = (LINES - grid_top);
1920     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1921     int row_limit;
1922     int per_row;
1923     char numbered[80];
1924     const char *hello;
1925     bool done = FALSE;
1926     bool opt_acsc = FALSE;
1927     bool opt_bold = FALSE;
1928     bool opt_wide = FALSE;
1929     bool opt_nums = FALSE;
1930     WINDOW *helpwin;
1931
1932     if (pairs_max > COLOR_PAIRS)
1933         pairs_max = COLOR_PAIRS;
1934
1935     while (!done) {
1936         int shown = 0;
1937
1938         /* this assumes an 80-column line */
1939         if (opt_wide) {
1940             width = 4;
1941             hello = "Test";
1942             per_row = (COLORS > 8) ? 16 : 8;
1943         } else {
1944             width = 8;
1945             hello = "Hello";
1946             per_row = 8;
1947         }
1948
1949         row_limit = (pairs_max + per_row - 1) / per_row;
1950
1951         move(0, 0);
1952         (void) printw("There are %d color pairs and %d colors\n",
1953                       pairs_max, COLORS);
1954
1955         clrtobot();
1956         (void) mvprintw(top + 1, 0,
1957                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1958                         row_limit,
1959                         per_row,
1960                         opt_bold ? "on" : "off");
1961
1962         /* show color names/numbers across the top */
1963         for (i = 0; i < per_row; i++)
1964             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
1965
1966         /* show a grid of colors, with color names/ numbers on the left */
1967         for (i = (short) (base_row * per_row); i < pairs_max; i++) {
1968             int row = grid_top + (i / per_row) - base_row;
1969             int col = (i % per_row + 1) * width;
1970             short pair = i;
1971
1972             if (row >= 0 && move(row, col) != ERR) {
1973                 short fg = (short) (i % COLORS);
1974                 short bg = (short) (i / COLORS);
1975
1976                 init_pair(pair, fg, bg);
1977                 attron((attr_t) COLOR_PAIR(pair));
1978                 if (opt_acsc)
1979                     attron((attr_t) A_ALTCHARSET);
1980                 if (opt_bold)
1981                     attron((attr_t) A_BOLD);
1982
1983                 if (opt_nums) {
1984                     sprintf(numbered, "{%02X}", i);
1985                     hello = numbered;
1986                 }
1987                 printw("%-*.*s", width, width, hello);
1988                 attrset(A_NORMAL);
1989
1990                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
1991                     show_color_name(row, 0, i / COLORS, opt_wide);
1992                 }
1993                 ++shown;
1994             } else if (shown) {
1995                 break;
1996             }
1997         }
1998
1999         switch (wGetchar(stdscr)) {
2000         case 'a':
2001             opt_acsc = FALSE;
2002             break;
2003         case 'A':
2004             opt_acsc = TRUE;
2005             break;
2006         case 'b':
2007             opt_bold = FALSE;
2008             break;
2009         case 'B':
2010             opt_bold = TRUE;
2011             break;
2012         case 'n':
2013             opt_nums = FALSE;
2014             break;
2015         case 'N':
2016             opt_nums = TRUE;
2017             break;
2018         case case_QUIT:
2019             done = TRUE;
2020             continue;
2021         case 'w':
2022             set_color_test(opt_wide, FALSE);
2023             break;
2024         case 'W':
2025             set_color_test(opt_wide, TRUE);
2026             break;
2027         case CTRL('p'):
2028         case KEY_UP:
2029             if (base_row <= 0) {
2030                 beep();
2031             } else {
2032                 base_row -= 1;
2033             }
2034             break;
2035         case CTRL('n'):
2036         case KEY_DOWN:
2037             if (base_row + page_size >= row_limit) {
2038                 beep();
2039             } else {
2040                 base_row += 1;
2041             }
2042             break;
2043         case CTRL('b'):
2044         case KEY_PREVIOUS:
2045         case KEY_PPAGE:
2046             if (base_row <= 0) {
2047                 beep();
2048             } else {
2049                 base_row -= (page_size - 1);
2050                 if (base_row < 0)
2051                     base_row = 0;
2052             }
2053             break;
2054         case CTRL('f'):
2055         case KEY_NEXT:
2056         case KEY_NPAGE:
2057             if (base_row + page_size >= row_limit) {
2058                 beep();
2059             } else {
2060                 base_row += page_size - 1;
2061                 if (base_row + page_size >= row_limit) {
2062                     base_row = row_limit - page_size - 1;
2063                 }
2064             }
2065             break;
2066         case '?':
2067             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2068                 box(helpwin, 0, 0);
2069                 color_legend(helpwin, FALSE);
2070                 wGetchar(helpwin);
2071                 delwin(helpwin);
2072             }
2073             break;
2074         default:
2075             beep();
2076             continue;
2077         }
2078     }
2079
2080     erase();
2081     endwin();
2082 }
2083
2084 #if USE_WIDEC_SUPPORT
2085 /* generate a color test pattern */
2086 static void
2087 wide_color_test(void)
2088 {
2089     int c;
2090     int i;
2091     int top = 0, width;
2092     int base_row = 0;
2093     int grid_top = top + 3;
2094     int page_size = (LINES - grid_top);
2095     int pairs_max = COLOR_PAIRS;
2096     int row_limit;
2097     int per_row;
2098     char numbered[80];
2099     const char *hello;
2100     bool done = FALSE;
2101     bool opt_acsc = FALSE;
2102     bool opt_bold = FALSE;
2103     bool opt_wide = FALSE;
2104     bool opt_nums = FALSE;
2105     bool opt_xchr = FALSE;
2106     wchar_t buffer[10];
2107     WINDOW *helpwin;
2108
2109     while (!done) {
2110         int shown = 0;
2111
2112         /* this assumes an 80-column line */
2113         if (opt_wide) {
2114             width = 4;
2115             hello = "Test";
2116             per_row = (COLORS > 8) ? 16 : 8;
2117         } else {
2118             width = 8;
2119             hello = "Hello";
2120             per_row = 8;
2121         }
2122         if (opt_xchr) {
2123             make_fullwidth_text(buffer, hello);
2124             width *= 2;
2125             per_row /= 2;
2126         } else {
2127             make_narrow_text(buffer, hello);
2128         }
2129
2130         row_limit = (pairs_max + per_row - 1) / per_row;
2131
2132         move(0, 0);
2133         (void) printw("There are %d color pairs and %d colors\n",
2134                       pairs_max, COLORS);
2135
2136         clrtobot();
2137         (void) mvprintw(top + 1, 0,
2138                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
2139                         row_limit,
2140                         per_row,
2141                         opt_bold ? "on" : "off");
2142
2143         /* show color names/numbers across the top */
2144         for (i = 0; i < per_row; i++)
2145             show_color_name(top + 2, (i + 1) * width, i, opt_wide);
2146
2147         /* show a grid of colors, with color names/ numbers on the left */
2148         for (i = (base_row * per_row); i < pairs_max; i++) {
2149             int row = grid_top + (i / per_row) - base_row;
2150             int col = (i % per_row + 1) * width;
2151             short pair = (short) i;
2152
2153             if (row >= 0 && move(row, col) != ERR) {
2154                 init_pair(pair, (short) (i % COLORS), (short) (i / COLORS));
2155                 color_set(pair, NULL);
2156                 if (opt_acsc)
2157                     attr_on((attr_t) A_ALTCHARSET, NULL);
2158                 if (opt_bold)
2159                     attr_on((attr_t) A_BOLD, NULL);
2160
2161                 if (opt_nums) {
2162                     sprintf(numbered, "{%02X}", i);
2163                     if (opt_xchr) {
2164                         make_fullwidth_text(buffer, numbered);
2165                     } else {
2166                         make_narrow_text(buffer, numbered);
2167                     }
2168                 }
2169                 addnwstr(buffer, width);
2170                 attr_set(A_NORMAL, 0, NULL);
2171
2172                 if ((i % per_row) == 0 && (i % COLORS) == 0) {
2173                     show_color_name(row, 0, i / COLORS, opt_wide);
2174                 }
2175                 ++shown;
2176             } else if (shown) {
2177                 break;
2178             }
2179         }
2180
2181         switch (c = wGetchar(stdscr)) {
2182         case 'a':
2183             opt_acsc = FALSE;
2184             break;
2185         case 'A':
2186             opt_acsc = TRUE;
2187             break;
2188         case 'b':
2189             opt_bold = FALSE;
2190             break;
2191         case 'B':
2192             opt_bold = TRUE;
2193             break;
2194         case 'n':
2195             opt_nums = FALSE;
2196             break;
2197         case 'N':
2198             opt_nums = TRUE;
2199             break;
2200         case case_QUIT:
2201             done = TRUE;
2202             continue;
2203         case 'w':
2204             set_color_test(opt_wide, FALSE);
2205             break;
2206         case 'W':
2207             set_color_test(opt_wide, TRUE);
2208             break;
2209         case 'x':
2210             opt_xchr = FALSE;
2211             break;
2212         case 'X':
2213             opt_xchr = TRUE;
2214             break;
2215         case CTRL('p'):
2216         case KEY_UP:
2217             if (base_row <= 0) {
2218                 beep();
2219             } else {
2220                 base_row -= 1;
2221             }
2222             break;
2223         case CTRL('n'):
2224         case KEY_DOWN:
2225             if (base_row + page_size >= row_limit) {
2226                 beep();
2227             } else {
2228                 base_row += 1;
2229             }
2230             break;
2231         case CTRL('b'):
2232         case KEY_PREVIOUS:
2233         case KEY_PPAGE:
2234             if (base_row <= 0) {
2235                 beep();
2236             } else {
2237                 base_row -= (page_size - 1);
2238                 if (base_row < 0)
2239                     base_row = 0;
2240             }
2241             break;
2242         case CTRL('f'):
2243         case KEY_NEXT:
2244         case KEY_NPAGE:
2245             if (base_row + page_size >= row_limit) {
2246                 beep();
2247             } else {
2248                 base_row += page_size - 1;
2249                 if (base_row + page_size >= row_limit) {
2250                     base_row = row_limit - page_size - 1;
2251                 }
2252             }
2253             break;
2254         case '?':
2255             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2256                 box(helpwin, 0, 0);
2257                 color_legend(helpwin, TRUE);
2258                 wGetchar(helpwin);
2259                 delwin(helpwin);
2260             }
2261             break;
2262         default:
2263             beep();
2264             continue;
2265         }
2266     }
2267
2268     erase();
2269     endwin();
2270 }
2271 #endif /* USE_WIDEC_SUPPORT */
2272
2273 static void
2274 change_color(short current, int field, int value, int usebase)
2275 {
2276     short red, green, blue;
2277
2278     color_content(current, &red, &green, &blue);
2279
2280     switch (field) {
2281     case 0:
2282         red = (short) (usebase ? (red + value) : value);
2283         break;
2284     case 1:
2285         green = (short) (usebase ? (green + value) : value);
2286         break;
2287     case 2:
2288         blue = (short) (usebase ? (blue + value) : value);
2289         break;
2290     }
2291
2292     if (init_color(current, red, green, blue) == ERR)
2293         beep();
2294 }
2295
2296 static void
2297 init_all_colors(void)
2298 {
2299     short c;
2300
2301     for (c = 0; c < COLORS; ++c)
2302         init_color(c,
2303                    all_colors[c].red,
2304                    all_colors[c].green,
2305                    all_colors[c].blue);
2306 }
2307
2308 #define scaled_rgb(n) ((255 * (n)) / 1000)
2309
2310 static void
2311 color_edit(void)
2312 /* display the color test pattern, without trying to edit colors */
2313 {
2314     int i;
2315     int current = 0;
2316     int this_c = 0, value = 0, field = 0;
2317     int last_c;
2318     int top_color = 0;
2319     int page_size = (LINES - 6);
2320
2321     init_all_colors();
2322     refresh();
2323
2324     for (i = 0; i < max_colors; i++)
2325         init_pair((short) i, (short) COLOR_WHITE, (short) i);
2326
2327     mvprintw(LINES - 2, 0, "Number: %d", value);
2328
2329     do {
2330         short red, green, blue;
2331
2332         attron(A_BOLD);
2333         mvaddstr(0, 20, "Color RGB Value Editing");
2334         attroff(A_BOLD);
2335
2336         for (i = (short) top_color;
2337              (i - top_color < page_size)
2338              && (i < max_colors); i++) {
2339             char numeric[80];
2340
2341             sprintf(numeric, "[%d]", i);
2342             mvprintw(2 + i - top_color, 0, "%c %-8s:",
2343                      (i == current ? '>' : ' '),
2344                      (i < (int) SIZEOF(the_color_names)
2345                       ? the_color_names[i] : numeric));
2346             attrset(COLOR_PAIR(i));
2347             addstr("        ");
2348             attrset(A_NORMAL);
2349
2350             color_content((short) i, &red, &green, &blue);
2351             addstr("   R = ");
2352             if (current == i && field == 0)
2353                 attron(A_STANDOUT);
2354             printw("%04d", red);
2355             if (current == i && field == 0)
2356                 attrset(A_NORMAL);
2357             addstr(", G = ");
2358             if (current == i && field == 1)
2359                 attron(A_STANDOUT);
2360             printw("%04d", green);
2361             if (current == i && field == 1)
2362                 attrset(A_NORMAL);
2363             addstr(", B = ");
2364             if (current == i && field == 2)
2365                 attron(A_STANDOUT);
2366             printw("%04d", blue);
2367             if (current == i && field == 2)
2368                 attrset(A_NORMAL);
2369             attrset(A_NORMAL);
2370             printw(" ( %3d %3d %3d )",
2371                    scaled_rgb(red),
2372                    scaled_rgb(green),
2373                    scaled_rgb(blue));
2374         }
2375
2376         mvaddstr(LINES - 3, 0,
2377                  "Use up/down to select a color, left/right to change fields.");
2378         mvaddstr(LINES - 2, 0,
2379                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2380
2381         move(2 + current - top_color, 0);
2382
2383         last_c = this_c;
2384         this_c = Getchar();
2385         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2386             value = 0;
2387
2388         switch (this_c) {
2389         case CTRL('b'):
2390         case KEY_PPAGE:
2391             if (current > 0)
2392                 current -= (page_size - 1);
2393             else
2394                 beep();
2395             break;
2396
2397         case CTRL('f'):
2398         case KEY_NPAGE:
2399             if (current < (max_colors - 1))
2400                 current += (page_size - 1);
2401             else
2402                 beep();
2403             break;
2404
2405         case CTRL('p'):
2406         case KEY_UP:
2407             current = (current == 0 ? (max_colors - 1) : current - 1);
2408             break;
2409
2410         case CTRL('n'):
2411         case KEY_DOWN:
2412             current = (current == (max_colors - 1) ? 0 : current + 1);
2413             break;
2414
2415         case KEY_RIGHT:
2416             field = (field == 2 ? 0 : field + 1);
2417             break;
2418
2419         case KEY_LEFT:
2420             field = (field == 0 ? 2 : field - 1);
2421             break;
2422
2423         case '0':
2424         case '1':
2425         case '2':
2426         case '3':
2427         case '4':
2428         case '5':
2429         case '6':
2430         case '7':
2431         case '8':
2432         case '9':
2433             value = value * 10 + (this_c - '0');
2434             break;
2435
2436         case '+':
2437             change_color((short) current, field, value, 1);
2438             break;
2439
2440         case '-':
2441             change_color((short) current, field, -value, 1);
2442             break;
2443
2444         case '=':
2445             change_color((short) current, field, value, 0);
2446             break;
2447
2448         case '?':
2449             erase();
2450             P("                      RGB Value Editing Help");
2451             P("");
2452             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2453             P("the fields in one of the RGB triples of the current colors; the one");
2454             P("currently selected will be reverse-video highlighted.");
2455             P("");
2456             P("To change a field, enter the digits of the new value; they are echoed");
2457             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2458             P("To increment or decrement a value, use the same procedure, but finish");
2459             P("with a `+' or `-'.");
2460             P("");
2461             P("Press 'm' to invoke the top-level menu with the current color settings.");
2462             P("To quit, do ESC");
2463
2464             Pause();
2465             erase();
2466             break;
2467
2468         case 'm':
2469             endwin();
2470             main_menu(FALSE);
2471             refresh();
2472             break;
2473
2474         case case_QUIT:
2475             break;
2476
2477         default:
2478             beep();
2479             break;
2480         }
2481
2482         if (current < 0)
2483             current = 0;
2484         if (current >= max_colors)
2485             current = max_colors - 1;
2486         if (current < top_color)
2487             top_color = current;
2488         if (current - top_color >= page_size)
2489             top_color = current - (page_size - 1);
2490
2491         mvprintw(LINES - 1, 0, "Number: %d", value);
2492         clrtoeol();
2493     } while
2494         (!isQuit(this_c));
2495
2496     erase();
2497
2498     /*
2499      * ncurses does not reset each color individually when calling endwin().
2500      */
2501     init_all_colors();
2502
2503     endwin();
2504 }
2505
2506 /****************************************************************************
2507  *
2508  * Soft-key label test
2509  *
2510  ****************************************************************************/
2511
2512 #if USE_SOFTKEYS
2513
2514 #define SLK_HELP 17
2515 #define SLK_WORK (SLK_HELP + 3)
2516
2517 static void
2518 slk_help(void)
2519 {
2520     static const char *table[] =
2521     {
2522         "Available commands are:"
2523         ,""
2524         ,"^L         -- repaint this message and activate soft keys"
2525         ,"a/d        -- activate/disable soft keys"
2526         ,"c          -- set centered format for labels"
2527         ,"l          -- set left-justified format for labels"
2528         ,"r          -- set right-justified format for labels"
2529         ,"[12345678] -- set label; labels are numbered 1 through 8"
2530         ,"e          -- erase stdscr (should not erase labels)"
2531         ,"s          -- test scrolling of shortened screen"
2532 #if HAVE_SLK_COLOR
2533         ,"F/B        -- cycle through foreground/background colors"
2534 #endif
2535         ,"ESC        -- return to main menu"
2536         ,""
2537         ,"Note: if activating the soft keys causes your terminal to scroll up"
2538         ,"one line, your terminal auto-scrolls when anything is written to the"
2539         ,"last screen position.  The ncurses code does not yet handle this"
2540         ,"gracefully."
2541     };
2542     unsigned j;
2543
2544     move(2, 0);
2545     for (j = 0; j < SIZEOF(table); ++j) {
2546         P(table[j]);
2547     }
2548     refresh();
2549 }
2550
2551 #if HAVE_SLK_COLOR
2552 static void
2553 call_slk_color(short fg, short bg)
2554 {
2555     init_pair(1, bg, fg);
2556     slk_color(1);
2557     mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2558     clrtoeol();
2559     refresh();
2560 }
2561 #endif
2562
2563 static void
2564 slk_test(void)
2565 /* exercise the soft keys */
2566 {
2567     int c, fmt = 1;
2568     char buf[9];
2569     char *s;
2570 #if HAVE_SLK_COLOR
2571     short fg = COLOR_BLACK;
2572     short bg = COLOR_WHITE;
2573 #endif
2574
2575     c = CTRL('l');
2576 #if HAVE_SLK_COLOR
2577     if (use_colors) {
2578         call_slk_color(fg, bg);
2579     }
2580 #endif
2581
2582     do {
2583         move(0, 0);
2584         switch (c) {
2585         case CTRL('l'):
2586             erase();
2587             attron(A_BOLD);
2588             mvaddstr(0, 20, "Soft Key Exerciser");
2589             attroff(A_BOLD);
2590
2591             slk_help();
2592             /* fall through */
2593
2594         case 'a':
2595             slk_restore();
2596             break;
2597
2598         case 'e':
2599             wclear(stdscr);
2600             break;
2601
2602         case 's':
2603             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2604             while ((c = Getchar()) != 'Q' && (c != ERR))
2605                 addch((chtype) c);
2606             break;
2607
2608         case 'd':
2609             slk_clear();
2610             break;
2611
2612         case 'l':
2613             fmt = 0;
2614             break;
2615
2616         case 'c':
2617             fmt = 1;
2618             break;
2619
2620         case 'r':
2621             fmt = 2;
2622             break;
2623
2624         case '1':
2625         case '2':
2626         case '3':
2627         case '4':
2628         case '5':
2629         case '6':
2630         case '7':
2631         case '8':
2632             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2633             strcpy(buf, "");
2634             if ((s = slk_label(c - '0')) != 0) {
2635                 strncpy(buf, s, 8);
2636             }
2637             wGetstring(stdscr, buf, 8);
2638             slk_set((c - '0'), buf, fmt);
2639             slk_refresh();
2640             move(SLK_WORK, 0);
2641             clrtobot();
2642             break;
2643
2644         case case_QUIT:
2645             goto done;
2646
2647 #if HAVE_SLK_COLOR
2648         case 'F':
2649             if (use_colors) {
2650                 fg = (short) ((fg + 1) % COLORS);
2651                 call_slk_color(fg, bg);
2652             }
2653             break;
2654         case 'B':
2655             if (use_colors) {
2656                 bg = (short) ((bg + 1) % COLORS);
2657                 call_slk_color(fg, bg);
2658             }
2659             break;
2660 #endif
2661 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2662         case KEY_RESIZE:
2663             wnoutrefresh(stdscr);
2664             break;
2665 #endif
2666
2667         default:
2668             beep();
2669         }
2670     } while (!isQuit(c = Getchar()));
2671
2672   done:
2673     slk_clear();
2674     erase();
2675     endwin();
2676 }
2677
2678 #if USE_WIDEC_SUPPORT
2679 #define SLKLEN 8
2680 static void
2681 wide_slk_test(void)
2682 /* exercise the soft keys */
2683 {
2684     int c, fmt = 1;
2685     wchar_t buf[SLKLEN + 1];
2686     char *s;
2687     short fg = COLOR_BLACK;
2688     short bg = COLOR_WHITE;
2689
2690     c = CTRL('l');
2691     if (use_colors) {
2692         call_slk_color(fg, bg);
2693     }
2694     do {
2695         move(0, 0);
2696         switch (c) {
2697         case CTRL('l'):
2698             erase();
2699             attr_on(WA_BOLD, NULL);
2700             mvaddstr(0, 20, "Soft Key Exerciser");
2701             attr_off(WA_BOLD, NULL);
2702
2703             slk_help();
2704             /* fall through */
2705
2706         case 'a':
2707             slk_restore();
2708             break;
2709
2710         case 'e':
2711             wclear(stdscr);
2712             break;
2713
2714         case 's':
2715             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2716             while ((c = Getchar()) != 'Q' && (c != ERR))
2717                 addch((chtype) c);
2718             break;
2719
2720         case 'd':
2721             slk_clear();
2722             break;
2723
2724         case 'l':
2725             fmt = 0;
2726             break;
2727
2728         case 'c':
2729             fmt = 1;
2730             break;
2731
2732         case 'r':
2733             fmt = 2;
2734             break;
2735
2736         case '1':
2737         case '2':
2738         case '3':
2739         case '4':
2740         case '5':
2741         case '6':
2742         case '7':
2743         case '8':
2744             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2745             *buf = 0;
2746             if ((s = slk_label(c - '0')) != 0) {
2747                 char *temp = strdup(s);
2748                 size_t used = strlen(temp);
2749                 size_t want = SLKLEN;
2750                 size_t test;
2751 #ifndef state_unused
2752                 mbstate_t state;
2753 #endif
2754
2755                 buf[0] = L'\0';
2756                 while (want > 0 && used != 0) {
2757                     const char *base = s;
2758                     reset_mbytes(state);
2759                     test = count_mbytes(base, 0, &state);
2760                     if (test == (size_t) -1) {
2761                         temp[--used] = 0;
2762                     } else if (test > want) {
2763                         temp[--used] = 0;
2764                     } else {
2765                         reset_mbytes(state);
2766                         trans_mbytes(buf, base, want, &state);
2767                         break;
2768                     }
2769                 }
2770                 free(temp);
2771             }
2772             wGet_wstring(stdscr, buf, SLKLEN);
2773             slk_wset((c - '0'), buf, fmt);
2774             slk_refresh();
2775             move(SLK_WORK, 0);
2776             clrtobot();
2777             break;
2778
2779         case case_QUIT:
2780             goto done;
2781
2782         case 'F':
2783             if (use_colors) {
2784                 fg = (short) ((fg + 1) % COLORS);
2785                 call_slk_color(fg, bg);
2786             }
2787             break;
2788         case 'B':
2789             if (use_colors) {
2790                 bg = (short) ((bg + 1) % COLORS);
2791                 call_slk_color(fg, bg);
2792             }
2793             break;
2794 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2795         case KEY_RESIZE:
2796             wnoutrefresh(stdscr);
2797             break;
2798 #endif
2799         default:
2800             beep();
2801         }
2802     } while (!isQuit(c = Getchar()));
2803
2804   done:
2805     slk_clear();
2806     erase();
2807     endwin();
2808 }
2809 #endif
2810 #endif /* SLK_INIT */
2811
2812 /****************************************************************************
2813  *
2814  * Alternate character-set stuff
2815  *
2816  ****************************************************************************/
2817 /* *INDENT-OFF* */
2818 static struct {
2819     chtype attr;
2820     const char *name;
2821 } attrs_to_cycle[] = {
2822     { A_NORMAL,         "normal" },
2823     { A_BOLD,           "bold" },
2824     { A_REVERSE,        "reverse" },
2825     { A_UNDERLINE,      "underline" },
2826 };
2827 /* *INDENT-ON* */
2828
2829 static bool
2830 cycle_attr(int ch, unsigned *at_code, chtype *attr)
2831 {
2832     bool result = TRUE;
2833
2834     switch (ch) {
2835     case 'v':
2836         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
2837             *at_code = 0;
2838         break;
2839     case 'V':
2840         if (*at_code == 1)
2841             *at_code = SIZEOF(attrs_to_cycle) - 1;
2842         else
2843             *at_code -= 1;
2844         break;
2845     default:
2846         result = FALSE;
2847         break;
2848     }
2849     if (result)
2850         *attr = attrs_to_cycle[*at_code].attr;
2851     return result;
2852 }
2853
2854 static bool
2855 cycle_colors(int ch, int *fg, int *bg, short *pair)
2856 {
2857     bool result = FALSE;
2858
2859     if (use_colors) {
2860         result = TRUE;
2861         switch (ch) {
2862         case 'F':
2863             if ((*fg -= 1) < 0)
2864                 *fg = COLORS - 1;
2865             break;
2866         case 'f':
2867             if ((*fg += 1) >= COLORS)
2868                 *fg = 0;
2869             break;
2870         case 'B':
2871             if ((*bg -= 1) < 0)
2872                 *bg = COLORS - 1;
2873             break;
2874         case 'b':
2875             if ((*bg += 1) >= COLORS)
2876                 *bg = 0;
2877             break;
2878         default:
2879             result = FALSE;
2880             break;
2881         }
2882         if (result) {
2883             *pair = (short) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2884             if (*pair != 0) {
2885                 *pair = 1;
2886                 if (init_pair(*pair, (short) *fg, (short) *bg) == ERR) {
2887                     result = FALSE;
2888                 }
2889             }
2890         }
2891     }
2892     return result;
2893 }
2894
2895 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2896  * terminal to perform functions.  The remaining codes can be graphic.
2897  */
2898 static void
2899 show_upper_chars(unsigned first, int repeat, attr_t attr, short pair)
2900 {
2901     bool C1 = (first == 128);
2902     unsigned code;
2903     unsigned last = first + 31;
2904     int reply;
2905
2906     erase();
2907     attron(A_BOLD);
2908     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2909              C1 ? "C1" : "GR", first, last);
2910     attroff(A_BOLD);
2911     refresh();
2912
2913     for (code = first; code <= last; code++) {
2914         int count = repeat;
2915         int row = 2 + ((int) (code - first) % 16);
2916         int col = ((int) (code - first) / 16) * COLS / 2;
2917         char tmp[80];
2918         sprintf(tmp, "%3u (0x%x)", code, code);
2919         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2920
2921         do {
2922             if (C1)
2923                 nodelay(stdscr, TRUE);
2924             echochar(code | attr | COLOR_PAIR(pair));
2925             if (C1) {
2926                 /* (yes, this _is_ crude) */
2927                 while ((reply = Getchar()) != ERR) {
2928                     addch(UChar(reply));
2929                     napms(10);
2930                 }
2931                 nodelay(stdscr, FALSE);
2932             }
2933         } while (--count > 0);
2934     }
2935 }
2936
2937 #define PC_COLS 4
2938
2939 static void
2940 show_pc_chars(int repeat, attr_t attr, short pair)
2941 {
2942     unsigned code;
2943
2944     erase();
2945     attron(A_BOLD);
2946     mvprintw(0, 20, "Display of PC Character Codes");
2947     attroff(A_BOLD);
2948     refresh();
2949
2950     for (code = 0; code < 16; ++code) {
2951         mvprintw(2, (int) code * PC_COLS + 8, "%X", code);
2952     }
2953     for (code = 0; code < 256; code++) {
2954         int count = repeat;
2955         int row = 3 + (int) (code / 16) + (code >= 128);
2956         int col = 8 + (int) (code % 16) * PC_COLS;
2957         if ((code % 16) == 0)
2958             mvprintw(row, 0, "0x%02x:", code);
2959         move(row, col);
2960         do {
2961             switch (code) {
2962             case '\n':
2963             case '\r':
2964             case '\b':
2965             case '\f':
2966             case '\033':
2967             case 0x9b:
2968                 /*
2969                  * Skip the ones that do not work.
2970                  */
2971                 break;
2972             default:
2973                 addch(code | A_ALTCHARSET | attr | COLOR_PAIR(pair));
2974                 break;
2975             }
2976         } while (--count > 0);
2977     }
2978 }
2979
2980 static void
2981 show_box_chars(int repeat, attr_t attr, short pair)
2982 {
2983     (void) repeat;
2984     attr |= COLOR_PAIR(pair);
2985
2986     erase();
2987     attron(A_BOLD);
2988     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
2989     attroff(A_BOLD);
2990     refresh();
2991     box(stdscr, 0, 0);
2992     /* *INDENT-OFF* */
2993     mvhline(LINES / 2, 0,        ACS_HLINE | attr, COLS);
2994     mvvline(0,         COLS / 2, ACS_VLINE | attr, LINES);
2995     mvaddch(0,         COLS / 2, ACS_TTEE | attr);
2996     mvaddch(LINES / 2, COLS / 2, ACS_PLUS | attr);
2997     mvaddch(LINES - 1, COLS / 2, ACS_BTEE | attr);
2998     mvaddch(LINES / 2, 0,        ACS_LTEE | attr);
2999     mvaddch(LINES / 2, COLS - 1, ACS_RTEE | attr);
3000     /* *INDENT-ON* */
3001
3002 }
3003
3004 static int
3005 show_1_acs(int n, int repeat, const char *name, chtype code)
3006 {
3007     const int height = 16;
3008     int row = 2 + (n % height);
3009     int col = (n / height) * COLS / 2;
3010
3011     mvprintw(row, col, "%*s : ", COLS / 4, name);
3012     do {
3013         addch(code);
3014     } while (--repeat > 0);
3015     return n + 1;
3016 }
3017
3018 static void
3019 show_acs_chars(int repeat, attr_t attr, short pair)
3020 /* display the ACS character set */
3021 {
3022     int n;
3023
3024 #define BOTH(name) #name, (name | attr | COLOR_PAIR(pair))
3025
3026     erase();
3027     attron(A_BOLD);
3028     mvaddstr(0, 20, "Display of the ACS Character Set");
3029     attroff(A_BOLD);
3030     refresh();
3031
3032     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3033     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3034     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3035     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3036
3037     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3038     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3039     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3040     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3041
3042     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3043     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3044
3045     /*
3046      * HPUX's ACS definitions are broken here.  Just give up.
3047      */
3048 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3049     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3050     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3051     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3052     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3053
3054     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3055     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3056     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3057     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3058     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3059     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3060     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3061     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3062     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3063
3064     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3065     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3066     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3067
3068     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3069     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3070     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3071     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3072     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3073     n = show_1_acs(n, repeat, BOTH(ACS_S9));
3074 #endif
3075 }
3076
3077 static void
3078 acs_display(void)
3079 {
3080     int c = 'a';
3081     char *term = getenv("TERM");
3082     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3083                               ? "p=PC, "
3084                               : "");
3085     chtype attr = A_NORMAL;
3086     int digit = 0;
3087     int repeat = 1;
3088     int fg = COLOR_BLACK;
3089     int bg = COLOR_BLACK;
3090     unsigned at_code = 0;
3091     short pair = 0;
3092     void (*last_show_acs) (int, attr_t, short) = 0;
3093
3094     do {
3095         switch (c) {
3096         case CTRL('L'):
3097             Repaint();
3098             break;
3099         case 'a':
3100             ToggleAcs(last_show_acs, show_acs_chars);
3101             break;
3102         case 'p':
3103             if (*pch_kludge)
3104                 ToggleAcs(last_show_acs, show_pc_chars);
3105             else
3106                 beep();
3107             break;
3108         case 'x':
3109             ToggleAcs(last_show_acs, show_box_chars);
3110             break;
3111         case '0':
3112         case '1':
3113         case '2':
3114         case '3':
3115             digit = (c - '0');
3116             last_show_acs = 0;
3117             break;
3118         case '-':
3119             if (digit > 0) {
3120                 --digit;
3121                 last_show_acs = 0;
3122             } else {
3123                 beep();
3124             }
3125             break;
3126         case '+':
3127             if (digit < 3) {
3128                 ++digit;
3129                 last_show_acs = 0;
3130             } else {
3131                 beep();
3132             }
3133             break;
3134         case '>':
3135             if (repeat < (COLS / 4))
3136                 ++repeat;
3137             break;
3138         case '<':
3139             if (repeat > 1)
3140                 --repeat;
3141             break;
3142         default:
3143             if (cycle_attr(c, &at_code, &attr)
3144                 || cycle_colors(c, &fg, &bg, &pair)) {
3145                 break;
3146             } else {
3147                 beep();
3148             }
3149             break;
3150         }
3151         if (last_show_acs != 0)
3152             last_show_acs(repeat, attr, pair);
3153         else
3154             show_upper_chars((unsigned) (digit * 32 + 128), repeat, attr, pair);
3155
3156         mvprintw(LINES - 3, 0,
3157                  "Note: ANSI terminals may not display C1 characters.");
3158         mvprintw(LINES - 2, 0,
3159                  "Select: a=ACS, x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3160                  pch_kludge);
3161         if (use_colors) {
3162             mvprintw(LINES - 1, 0,
3163                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3164                      attrs_to_cycle[at_code].name,
3165                      fg, bg);
3166         } else {
3167             mvprintw(LINES - 1, 0,
3168                      "v/V cycles through video attributes (%s).",
3169                      attrs_to_cycle[at_code].name);
3170         }
3171         refresh();
3172     } while (!isQuit(c = Getchar()));
3173
3174     Pause();
3175     erase();
3176     endwin();
3177 }
3178
3179 #if USE_WIDEC_SUPPORT
3180 static cchar_t *
3181 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, short pair)
3182 {
3183     int count = getcchar(src, NULL, NULL, NULL, 0);
3184     wchar_t *wch = 0;
3185     attr_t ignore_attr;
3186     short ignore_pair;
3187
3188     *dst = *src;
3189     if (count > 0) {
3190         if ((wch = typeMalloc(wchar_t, (unsigned) count + 1)) != 0) {
3191             if (getcchar(src, wch, &ignore_attr, &ignore_pair, 0) != ERR) {
3192                 attr |= (ignore_attr & A_ALTCHARSET);
3193                 setcchar(dst, wch, attr, pair, 0);
3194             }
3195             free(wch);
3196         }
3197     }
3198     return dst;
3199 }
3200
3201 static void
3202 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
3203 {
3204     cchar_t temp;
3205     wchar_t code;
3206     int last = first + 31;
3207
3208     erase();
3209     attron(A_BOLD);
3210     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
3211     attroff(A_BOLD);
3212
3213     for (code = first; (int) code <= last; code++) {
3214         int row = 2 + ((code - first) % 16);
3215         int col = ((code - first) / 16) * COLS / 2;
3216         wchar_t codes[10];
3217         char tmp[80];
3218         int count = repeat;
3219         int y, x;
3220
3221         memset(&codes, 0, sizeof(codes));
3222         codes[0] = code;
3223         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3224         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
3225         setcchar(&temp, codes, attr, pair, 0);
3226         do {
3227             /*
3228              * Give non-spacing characters something to combine with.  If we
3229              * don't, they'll bunch up in a heap on the space after the ":".
3230              * Mark them with reverse-video to make them simpler to find on
3231              * the display.
3232              */
3233             if (wcwidth(code) == 0)
3234                 addch(space | A_REVERSE);
3235             /*
3236              * This could use add_wch(), but is done for comparison with the
3237              * normal 'f' test (and to make a test-case for echo_wchar()).
3238              * The screen will flicker because the erase() at the top of the
3239              * function is met by the builtin refresh() in echo_wchar().
3240              */
3241             echo_wchar(&temp);
3242             /*
3243              * The repeat-count may make text wrap - avoid that.
3244              */
3245             getyx(stdscr, y, x);
3246             if (x >= col + (COLS / 2) - 2)
3247                 break;
3248         } while (--count > 0);
3249     }
3250 }
3251
3252 static int
3253 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3254 {
3255     const int height = 16;
3256     int row = 2 + (n % height);
3257     int col = (n / height) * COLS / 2;
3258
3259     mvprintw(row, col, "%*s : ", COLS / 4, name);
3260     while (--repeat >= 0) {
3261         add_wch(code);
3262     }
3263     return n + 1;
3264 }
3265
3266 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3267
3268 static void
3269 show_wacs_chars(int repeat, attr_t attr, short pair)
3270 /* display the wide-ACS character set */
3271 {
3272     cchar_t temp;
3273
3274     int n;
3275
3276 /*#define BOTH2(name) #name, &(name) */
3277 #define BOTH2(name) #name, MERGE_ATTR(name)
3278
3279     erase();
3280     attron(A_BOLD);
3281     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3282     attroff(A_BOLD);
3283     refresh();
3284
3285     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3286     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3287     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3288     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3289
3290     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3291     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3292     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3293     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3294
3295     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3296     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3297
3298     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3299     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3300     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3301     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3302
3303     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3304     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3305     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3306     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3307     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3308     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3309     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3310     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3311     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3312
3313 #ifdef CURSES_WACS_ARRAY
3314     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3315     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3316     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3317
3318     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3319     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3320     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3321     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3322     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3323     n = show_1_wacs(n, repeat, BOTH2(WACS_S9));
3324 #endif
3325 }
3326
3327 #undef MERGE_ATTR
3328
3329 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3330
3331 static void
3332 show_wbox_chars(int repeat, attr_t attr, short pair)
3333 {
3334     cchar_t temp;
3335
3336     (void) repeat;
3337     erase();
3338     attron(A_BOLD);
3339     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3340     attroff(A_BOLD);
3341     refresh();
3342
3343     attr_set(attr, pair, 0);
3344     box_set(stdscr, 0, 0);
3345     attr_set(A_NORMAL, 0, 0);
3346     /* *INDENT-OFF* */
3347     mvhline_set(LINES / 2, 0,        MERGE_ATTR(WACS_HLINE), COLS);
3348     mvvline_set(0,         COLS / 2, MERGE_ATTR(WACS_VLINE), LINES);
3349     mvadd_wch(0,           COLS / 2, MERGE_ATTR(WACS_TTEE));
3350     mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(WACS_PLUS));
3351     mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(WACS_BTEE));
3352     mvadd_wch(LINES / 2,   0,        MERGE_ATTR(WACS_LTEE));
3353     mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(WACS_RTEE));
3354     /* *INDENT-ON* */
3355
3356 }
3357
3358 #undef MERGE_ATTR
3359
3360 static int
3361 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3362 {
3363     const int height = 16;
3364     int row = 2 + (n % height);
3365     int col = (n / height) * COLS / 2;
3366     char temp[80];
3367
3368     mvprintw(row, col, "%*s : ", COLS / 4, name);
3369     attr_set(attr, pair, 0);
3370     addstr(strcpy(temp, code));
3371     attr_set(A_NORMAL, 0, 0);
3372     return n + 1;
3373 }
3374
3375 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3376
3377 static void
3378 show_utf8_chars(int repeat, attr_t attr, short pair)
3379 {
3380     int n;
3381
3382     (void) repeat;
3383     erase();
3384     attron(A_BOLD);
3385     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3386     attroff(A_BOLD);
3387     refresh();
3388     /* *INDENT-OFF* */
3389     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3390     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3391     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3392     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3393
3394     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3395     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3396     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3397     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3398
3399     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3400     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3401
3402     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3403     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3404     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3405     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3406
3407     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3408     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3409     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3410     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3411     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3412     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3413     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3414     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3415     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3416     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3417     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3418     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3419
3420     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3421     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3422     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3423     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3424     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3425     n = SHOW_UTF8(n, "WACS_S9",         "\342\216\275");
3426     /* *INDENT-ON* */
3427
3428 }
3429
3430 /* display the wide-ACS character set */
3431 static void
3432 wide_acs_display(void)
3433 {
3434     int c = 'a';
3435     int digit = 0;
3436     int repeat = 1;
3437     int space = ' ';
3438     chtype attr = A_NORMAL;
3439     int fg = COLOR_BLACK;
3440     int bg = COLOR_BLACK;
3441     unsigned at_code = 0;
3442     short pair = 0;
3443     void (*last_show_wacs) (int, attr_t, short) = 0;
3444
3445     do {
3446         switch (c) {
3447         case CTRL('L'):
3448             Repaint();
3449             break;
3450         case 'a':
3451             ToggleAcs(last_show_wacs, show_wacs_chars);
3452             break;
3453         case 'x':
3454             ToggleAcs(last_show_wacs, show_wbox_chars);
3455             break;
3456         case 'u':
3457             ToggleAcs(last_show_wacs, show_utf8_chars);
3458             break;
3459         default:
3460             if (c < 256 && isdigit(c)) {
3461                 digit = (c - '0');
3462                 last_show_wacs = 0;
3463             } else if (c == '+') {
3464                 ++digit;
3465                 last_show_wacs = 0;
3466             } else if (c == '-' && digit > 0) {
3467                 --digit;
3468                 last_show_wacs = 0;
3469             } else if (c == '>' && repeat < (COLS / 4)) {
3470                 ++repeat;
3471             } else if (c == '<' && repeat > 1) {
3472                 --repeat;
3473             } else if (c == '_') {
3474                 space = (space == ' ') ? '_' : ' ';
3475                 last_show_wacs = 0;
3476             } else if (cycle_attr(c, &at_code, &attr)
3477                        || cycle_colors(c, &fg, &bg, &pair)) {
3478                 if (last_show_wacs != 0)
3479                     break;
3480             } else {
3481                 beep();
3482                 break;
3483             }
3484             break;
3485         }
3486         if (last_show_wacs != 0)
3487             last_show_wacs(repeat, attr, pair);
3488         else
3489             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3490
3491         mvprintw(LINES - 3, 0,
3492                  "Select: a WACS, x box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, ESC=quit");
3493         if (use_colors) {
3494             mvprintw(LINES - 2, 0,
3495                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3496                      attrs_to_cycle[at_code].name,
3497                      fg, bg);
3498         } else {
3499             mvprintw(LINES - 2, 0,
3500                      "v/V cycles through video attributes (%s).",
3501                      attrs_to_cycle[at_code].name);
3502         }
3503         refresh();
3504     } while (!isQuit(c = Getchar()));
3505
3506     Pause();
3507     erase();
3508     endwin();
3509 }
3510
3511 #endif
3512
3513 /*
3514  * Graphic-rendition test (adapted from vttest)
3515  */
3516 static void
3517 test_sgr_attributes(void)
3518 {
3519     int pass;
3520
3521     for (pass = 0; pass < 2; pass++) {
3522         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3523
3524         /* Use non-default colors if possible to exercise bce a little */
3525         if (use_colors) {
3526             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3527             normal |= COLOR_PAIR(1);
3528         }
3529         bkgdset(normal);
3530         erase();
3531         mvprintw(1, 20, "Graphic rendition test pattern:");
3532
3533         mvprintw(4, 1, "vanilla");
3534
3535 #define set_sgr(mask) bkgdset((normal^(mask)));
3536         set_sgr(A_BOLD);
3537         mvprintw(4, 40, "bold");
3538
3539         set_sgr(A_UNDERLINE);
3540         mvprintw(6, 6, "underline");
3541
3542         set_sgr(A_BOLD | A_UNDERLINE);
3543         mvprintw(6, 45, "bold underline");
3544
3545         set_sgr(A_BLINK);
3546         mvprintw(8, 1, "blink");
3547
3548         set_sgr(A_BLINK | A_BOLD);
3549         mvprintw(8, 40, "bold blink");
3550
3551         set_sgr(A_UNDERLINE | A_BLINK);
3552         mvprintw(10, 6, "underline blink");
3553
3554         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3555         mvprintw(10, 45, "bold underline blink");
3556
3557         set_sgr(A_REVERSE);
3558         mvprintw(12, 1, "negative");
3559
3560         set_sgr(A_BOLD | A_REVERSE);
3561         mvprintw(12, 40, "bold negative");
3562
3563         set_sgr(A_UNDERLINE | A_REVERSE);
3564         mvprintw(14, 6, "underline negative");
3565
3566         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3567         mvprintw(14, 45, "bold underline negative");
3568
3569         set_sgr(A_BLINK | A_REVERSE);
3570         mvprintw(16, 1, "blink negative");
3571
3572         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3573         mvprintw(16, 40, "bold blink negative");
3574
3575         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3576         mvprintw(18, 6, "underline blink negative");
3577
3578         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3579         mvprintw(18, 45, "bold underline blink negative");
3580
3581         bkgdset(normal);
3582         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3583                  "Light");
3584         clrtoeol();
3585         Pause();
3586     }
3587
3588     bkgdset(A_NORMAL | BLANK);
3589     erase();
3590     endwin();
3591 }
3592
3593 /****************************************************************************
3594  *
3595  * Windows and scrolling tester.
3596  *
3597  ****************************************************************************/
3598
3599 #define BOTLINES        4       /* number of line stolen from screen bottom */
3600
3601 typedef struct {
3602     int y, x;
3603 } pair;
3604
3605 #define FRAME struct frame
3606 FRAME
3607 {
3608     FRAME *next, *last;
3609     bool do_scroll;
3610     bool do_keypad;
3611     WINDOW *wind;
3612 };
3613
3614 #if defined(NCURSES_VERSION)
3615 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
3616 #define is_keypad(win)   (win)->_use_keypad
3617 #define is_scrollok(win) (win)->_scroll
3618 #elif !defined(is_keypad)
3619 #define is_keypad(win)   FALSE
3620 #define is_scrollok(win) FALSE
3621 #endif
3622 #else
3623 #define is_keypad(win)   FALSE
3624 #define is_scrollok(win) FALSE
3625 #endif
3626
3627 static WINDOW *
3628 frame_win(FRAME * curp)
3629 {
3630     return (curp != 0) ? curp->wind : stdscr;
3631 }
3632
3633 /* We need to know if these flags are actually set, so don't look in FRAME.
3634  * These names are known to work with SVr4 curses as well as ncurses.  The
3635  * _use_keypad name does not work with Solaris 8.
3636  */
3637 static bool
3638 HaveKeypad(FRAME * curp)
3639 {
3640     WINDOW *win = frame_win(curp);
3641     (void) win;
3642     return is_keypad(win);
3643 }
3644
3645 static bool
3646 HaveScroll(FRAME * curp)
3647 {
3648     WINDOW *win = frame_win(curp);
3649     (void) win;
3650     return is_scrollok(win);
3651 }
3652
3653 static void
3654 newwin_legend(FRAME * curp)
3655 {
3656     static const struct {
3657         const char *msg;
3658         int code;
3659     } legend[] = {
3660         {
3661             "^C = create window", 0
3662         },
3663         {
3664             "^N = next window", 0
3665         },
3666         {
3667             "^P = previous window", 0
3668         },
3669         {
3670             "^F = scroll forward", 0
3671         },
3672         {
3673             "^B = scroll backward", 0
3674         },
3675         {
3676             "^K = keypad(%s)", 1
3677         },
3678         {
3679             "^S = scrollok(%s)", 2
3680         },
3681         {
3682             "^W = save window to file", 0
3683         },
3684         {
3685             "^R = restore window", 0
3686         },
3687 #if HAVE_WRESIZE
3688         {
3689             "^X = resize", 0
3690         },
3691 #endif
3692         {
3693             "^Q%s = exit", 3
3694         }
3695     };
3696     size_t n;
3697     int x;
3698     bool do_keypad = HaveKeypad(curp);
3699     bool do_scroll = HaveScroll(curp);
3700     char buf[BUFSIZ];
3701
3702     move(LINES - 4, 0);
3703     for (n = 0; n < SIZEOF(legend); n++) {
3704         switch (legend[n].code) {
3705         default:
3706             strcpy(buf, legend[n].msg);
3707             break;
3708         case 1:
3709             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3710             break;
3711         case 2:
3712             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3713             break;
3714         case 3:
3715             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3716             break;
3717         }
3718         x = getcurx(stdscr);
3719         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3720         addstr(buf);
3721     }
3722     clrtoeol();
3723 }
3724
3725 static void
3726 transient(FRAME * curp, NCURSES_CONST char *msg)
3727 {
3728     newwin_legend(curp);
3729     if (msg) {
3730         mvaddstr(LINES - 1, 0, msg);
3731         refresh();
3732         napms(1000);
3733     }
3734
3735     move(LINES - 1, 0);
3736     printw("%s characters are echoed, window should %sscroll.",
3737            HaveKeypad(curp) ? "Non-arrow" : "All other",
3738            HaveScroll(curp) ? "" : "not ");
3739     clrtoeol();
3740 }
3741
3742 static void
3743 newwin_report(FRAME * curp)
3744 /* report on the cursor's current position, then restore it */
3745 {
3746     WINDOW *win = frame_win(curp);
3747     int y, x;
3748
3749     if (win != stdscr)
3750         transient(curp, (char *) 0);
3751     getyx(win, y, x);
3752     move(LINES - 1, COLS - 17);
3753     printw("Y = %2d X = %2d", y, x);
3754     if (win != stdscr)
3755         refresh();
3756     else
3757         wmove(win, y, x);
3758 }
3759
3760 static pair *
3761 selectcell(int uli, int ulj, int lri, int lrj)
3762 /* arrows keys move cursor, return location at current on non-arrow key */
3763 {
3764     static pair res;            /* result cell */
3765     int si = lri - uli + 1;     /* depth of the select area */
3766     int sj = lrj - ulj + 1;     /* width of the select area */
3767     int i = 0, j = 0;           /* offsets into the select area */
3768
3769     res.y = uli;
3770     res.x = ulj;
3771     for (;;) {
3772         move(uli + i, ulj + j);
3773         newwin_report((FRAME *) 0);
3774
3775         switch (Getchar()) {
3776         case KEY_UP:
3777             i += si - 1;
3778             break;
3779         case KEY_DOWN:
3780             i++;
3781             break;
3782         case KEY_LEFT:
3783             j += sj - 1;
3784             break;
3785         case KEY_RIGHT:
3786             j++;
3787             break;
3788         case case_QUIT:
3789             return ((pair *) 0);
3790 #ifdef NCURSES_MOUSE_VERSION
3791         case KEY_MOUSE:
3792             {
3793                 MEVENT event;
3794
3795                 getmouse(&event);
3796                 if (event.y > uli && event.x > ulj) {
3797                     i = event.y - uli;
3798                     j = event.x - ulj;
3799                 } else {
3800                     beep();
3801                     break;
3802                 }
3803             }
3804             /* FALLTHRU */
3805 #endif
3806         default:
3807             res.y = uli + i;
3808             res.x = ulj + j;
3809             return (&res);
3810         }
3811         i %= si;
3812         j %= sj;
3813     }
3814 }
3815
3816 static void
3817 outerbox(pair ul, pair lr, bool onoff)
3818 /* draw or erase a box *outside* the given pair of corners */
3819 {
3820     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3821     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3822     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3823     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3824     move(ul.y - 1, ul.x);
3825     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3826     move(ul.y, ul.x - 1);
3827     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3828     move(lr.y + 1, ul.x);
3829     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3830     move(ul.y, lr.x + 1);
3831     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3832 }
3833
3834 static WINDOW *
3835 getwindow(void)
3836 /* Ask user for a window definition */
3837 {
3838     WINDOW *rwindow;
3839     pair ul, lr, *tmp;
3840
3841     move(0, 0);
3842     clrtoeol();
3843     addstr("Use arrows to move cursor, anything else to mark corner 1");
3844     refresh();
3845     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3846         return ((WINDOW *) 0);
3847     memcpy(&ul, tmp, sizeof(pair));
3848     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3849     move(0, 0);
3850     clrtoeol();
3851     addstr("Use arrows to move cursor, anything else to mark corner 2");
3852     refresh();
3853     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3854         (pair *) 0)
3855         return ((WINDOW *) 0);
3856     memcpy(&lr, tmp, sizeof(pair));
3857
3858     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3859
3860     outerbox(ul, lr, TRUE);
3861     refresh();
3862
3863     wrefresh(rwindow);
3864
3865     move(0, 0);
3866     clrtoeol();
3867     return (rwindow);
3868 }
3869
3870 static void
3871 newwin_move(FRAME * curp, int dy, int dx)
3872 {
3873     WINDOW *win = frame_win(curp);
3874     int cur_y, cur_x;
3875     int max_y, max_x;
3876
3877     getyx(win, cur_y, cur_x);
3878     getmaxyx(win, max_y, max_x);
3879     if ((cur_x += dx) < 0)
3880         cur_x = 0;
3881     else if (cur_x >= max_x)
3882         cur_x = max_x - 1;
3883     if ((cur_y += dy) < 0)
3884         cur_y = 0;
3885     else if (cur_y >= max_y)
3886         cur_y = max_y - 1;
3887     wmove(win, cur_y, cur_x);
3888 }
3889
3890 static FRAME *
3891 delete_framed(FRAME * fp, bool showit)
3892 {
3893     FRAME *np = 0;
3894
3895     if (fp != 0) {
3896         fp->last->next = fp->next;
3897         fp->next->last = fp->last;
3898
3899         if (showit) {
3900             werase(fp->wind);
3901             wrefresh(fp->wind);
3902         }
3903         delwin(fp->wind);
3904
3905         np = (fp == fp->next) ? 0 : fp->next;
3906         free(fp);
3907     }
3908     return np;
3909 }
3910
3911 static void
3912 acs_and_scroll(void)
3913 /* Demonstrate windows */
3914 {
3915     int c;
3916     FRAME *current = (FRAME *) 0, *neww;
3917     WINDOW *usescr = stdscr;
3918 #if HAVE_PUTWIN && HAVE_GETWIN
3919     FILE *fp;
3920 #endif
3921
3922 #define DUMPFILE        "screendump"
3923
3924 #ifdef NCURSES_MOUSE_VERSION
3925     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3926 #endif
3927     c = CTRL('C');
3928     raw();
3929     do {
3930         transient((FRAME *) 0, (char *) 0);
3931         switch (c) {
3932         case CTRL('C'):
3933             if ((neww = typeCalloc(FRAME, 1)) == 0) {
3934                 goto breakout;
3935             }
3936             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
3937                 free(neww);
3938                 goto breakout;
3939             }
3940
3941             if (current == 0) { /* First element,  */
3942                 neww->next = neww;      /*   so point it at itself */
3943                 neww->last = neww;
3944             } else {
3945                 neww->next = current->next;
3946                 neww->last = current;
3947                 neww->last->next = neww;
3948                 neww->next->last = neww;
3949             }
3950             current = neww;
3951             /* SVr4 curses sets the keypad on all newly-created windows to
3952              * false.  Someone reported that PDCurses makes new windows inherit
3953              * this flag.  Remove the following 'keypad()' call to test this
3954              */
3955             keypad(current->wind, TRUE);
3956             current->do_keypad = HaveKeypad(current);
3957             current->do_scroll = HaveScroll(current);
3958             break;
3959
3960         case CTRL('N'): /* go to next window */
3961             if (current)
3962                 current = current->next;
3963             break;
3964
3965         case CTRL('P'): /* go to previous window */
3966             if (current)
3967                 current = current->last;
3968             break;
3969
3970         case CTRL('F'): /* scroll current window forward */
3971             if (current)
3972                 wscrl(frame_win(current), 1);
3973             break;
3974
3975         case CTRL('B'): /* scroll current window backwards */
3976             if (current)
3977                 wscrl(frame_win(current), -1);
3978             break;
3979
3980         case CTRL('K'): /* toggle keypad mode for current */
3981             if (current) {
3982                 current->do_keypad = !current->do_keypad;
3983                 keypad(current->wind, current->do_keypad);
3984             }
3985             break;
3986
3987         case CTRL('S'):
3988             if (current) {
3989                 current->do_scroll = !current->do_scroll;
3990                 scrollok(current->wind, current->do_scroll);
3991             }
3992             break;
3993
3994 #if HAVE_PUTWIN && HAVE_GETWIN
3995         case CTRL('W'): /* save and delete window */
3996             if ((current != 0) && (current == current->next)) {
3997                 transient(current, "Will not save/delete ONLY window");
3998                 break;
3999             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4000                 transient(current, "Can't open screen dump file");
4001             } else {
4002                 (void) putwin(frame_win(current), fp);
4003                 (void) fclose(fp);
4004
4005                 current = delete_framed(current, TRUE);
4006             }
4007             break;
4008
4009         case CTRL('R'): /* restore window */
4010             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4011                 transient(current, "Can't open screen dump file");
4012             } else {
4013                 if ((neww = typeCalloc(FRAME, 1)) != 0) {
4014
4015                     neww->next = current ? current->next : 0;
4016                     neww->last = current;
4017                     neww->last->next = neww;
4018                     neww->next->last = neww;
4019
4020                     neww->wind = getwin(fp);
4021
4022                     wrefresh(neww->wind);
4023                 }
4024                 (void) fclose(fp);
4025             }
4026             break;
4027 #endif
4028
4029 #if HAVE_WRESIZE
4030         case CTRL('X'): /* resize window */
4031             if (current) {
4032                 pair *tmp, ul, lr;
4033                 int i, mx, my;
4034
4035                 move(0, 0);
4036                 clrtoeol();
4037                 addstr("Use arrows to move cursor, anything else to mark new corner");
4038                 refresh();
4039
4040                 getbegyx(current->wind, ul.y, ul.x);
4041
4042                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4043                 if (tmp == (pair *) 0) {
4044                     beep();
4045                     break;
4046                 }
4047
4048                 getmaxyx(current->wind, lr.y, lr.x);
4049                 lr.y += (ul.y - 1);
4050                 lr.x += (ul.x - 1);
4051                 outerbox(ul, lr, FALSE);
4052                 wnoutrefresh(stdscr);
4053
4054                 /* strictly cosmetic hack for the test */
4055                 getmaxyx(current->wind, my, mx);
4056                 if (my > tmp->y - ul.y) {
4057                     getyx(current->wind, lr.y, lr.x);
4058                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4059                     wclrtobot(current->wind);
4060                     wmove(current->wind, lr.y, lr.x);
4061                 }
4062                 if (mx > tmp->x - ul.x)
4063                     for (i = 0; i < my; i++) {
4064                         wmove(current->wind, i, tmp->x - ul.x + 1);
4065                         wclrtoeol(current->wind);
4066                     }
4067                 wnoutrefresh(current->wind);
4068
4069                 memcpy(&lr, tmp, sizeof(pair));
4070                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4071
4072                 getbegyx(current->wind, ul.y, ul.x);
4073                 getmaxyx(current->wind, lr.y, lr.x);
4074                 lr.y += (ul.y - 1);
4075                 lr.x += (ul.x - 1);
4076                 outerbox(ul, lr, TRUE);
4077                 wnoutrefresh(stdscr);
4078
4079                 wnoutrefresh(current->wind);
4080                 move(0, 0);
4081                 clrtoeol();
4082                 doupdate();
4083             }
4084             break;
4085 #endif /* HAVE_WRESIZE */
4086
4087         case KEY_F(10): /* undocumented --- use this to test area clears */
4088             selectcell(0, 0, LINES - 1, COLS - 1);
4089             clrtobot();
4090             refresh();
4091             break;
4092
4093         case KEY_UP:
4094             newwin_move(current, -1, 0);
4095             break;
4096         case KEY_DOWN:
4097             newwin_move(current, 1, 0);
4098             break;
4099         case KEY_LEFT:
4100             newwin_move(current, 0, -1);
4101             break;
4102         case KEY_RIGHT:
4103             newwin_move(current, 0, 1);
4104             break;
4105
4106         case KEY_BACKSPACE:
4107             /* FALLTHROUGH */
4108         case KEY_DC:
4109             {
4110                 int y, x;
4111                 getyx(frame_win(current), y, x);
4112                 if (--x < 0) {
4113                     if (--y < 0)
4114                         break;
4115                     x = getmaxx(frame_win(current)) - 1;
4116                 }
4117                 mvwdelch(frame_win(current), y, x);
4118             }
4119             break;
4120
4121         case '\r':
4122             c = '\n';
4123             /* FALLTHROUGH */
4124
4125         default:
4126             if (current)
4127                 waddch(current->wind, (chtype) c);
4128             else
4129                 beep();
4130             break;
4131         }
4132         newwin_report(current);
4133         usescr = frame_win(current);
4134         wrefresh(usescr);
4135     } while
4136         (!isQuit(c = wGetchar(usescr))
4137          && (c != ERR));
4138
4139   breakout:
4140     while (current != 0)
4141         current = delete_framed(current, FALSE);
4142
4143     scrollok(stdscr, TRUE);     /* reset to driver's default */
4144 #ifdef NCURSES_MOUSE_VERSION
4145     mousemask(0, (mmask_t *) 0);
4146 #endif
4147     noraw();
4148     erase();
4149     endwin();
4150 }
4151
4152 /****************************************************************************
4153  *
4154  * Panels tester
4155  *
4156  ****************************************************************************/
4157
4158 #if USE_LIBPANEL
4159 static int nap_msec = 1;
4160
4161 static NCURSES_CONST char *mod[] =
4162 {
4163     "test ",
4164     "TEST ",
4165     "(**) ",
4166     "*()* ",
4167     "<--> ",
4168     "LAST "
4169 };
4170
4171 /*+-------------------------------------------------------------------------
4172         wait_a_while(msec)
4173 --------------------------------------------------------------------------*/
4174 static void
4175 wait_a_while(int msec GCC_UNUSED)
4176 {
4177 #if HAVE_NAPMS
4178     if (nap_msec == 1)
4179         wGetchar(stdscr);
4180     else
4181         napms(nap_msec);
4182 #else
4183     if (nap_msec == 1)
4184         wGetchar(stdscr);
4185     else if (msec > 1000)
4186         sleep((unsigned) msec / 1000);
4187     else
4188         sleep(1);
4189 #endif
4190 }                               /* end of wait_a_while */
4191
4192 /*+-------------------------------------------------------------------------
4193         saywhat(text)
4194 --------------------------------------------------------------------------*/
4195 static void
4196 saywhat(NCURSES_CONST char *text)
4197 {
4198     wmove(stdscr, LINES - 1, 0);
4199     wclrtoeol(stdscr);
4200     if (text != 0 && *text != '\0') {
4201         waddstr(stdscr, text);
4202         waddstr(stdscr, "; ");
4203     }
4204     waddstr(stdscr, "press any key to continue");
4205 }                               /* end of saywhat */
4206
4207 /*+-------------------------------------------------------------------------
4208         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4209 --------------------------------------------------------------------------*/
4210 static PANEL *
4211 mkpanel(short color, int rows, int cols, int tly, int tlx)
4212 {
4213     WINDOW *win;
4214     PANEL *pan = 0;
4215
4216     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4217         if ((pan = new_panel(win)) == 0) {
4218             delwin(win);
4219         } else if (use_colors) {
4220             short fg = (short) ((color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
4221             short bg = color;
4222
4223             init_pair(color, fg, bg);
4224             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
4225         } else {
4226             wbkgdset(win, A_BOLD | ' ');
4227         }
4228     }
4229     return pan;
4230 }                               /* end of mkpanel */
4231
4232 /*+-------------------------------------------------------------------------
4233         rmpanel(pan)
4234 --------------------------------------------------------------------------*/
4235 static void
4236 rmpanel(PANEL * pan)
4237 {
4238     WINDOW *win = panel_window(pan);
4239     del_panel(pan);
4240     delwin(win);
4241 }                               /* end of rmpanel */
4242
4243 /*+-------------------------------------------------------------------------
4244         pflush()
4245 --------------------------------------------------------------------------*/
4246 static void
4247 pflush(void)
4248 {
4249     update_panels();
4250     doupdate();
4251 }                               /* end of pflush */
4252
4253 /*+-------------------------------------------------------------------------
4254         fill_panel(win)
4255 --------------------------------------------------------------------------*/
4256 static void
4257 init_panel(void)
4258 {
4259     register int y, x;
4260
4261     for (y = 0; y < LINES - 1; y++) {
4262         for (x = 0; x < COLS; x++)
4263             wprintw(stdscr, "%d", (y + x) % 10);
4264     }
4265 }
4266
4267 static void
4268 fill_panel(PANEL * pan)
4269 {
4270     WINDOW *win = panel_window(pan);
4271     const char *userptr = (const char *) panel_userptr(pan);
4272     int num = (userptr && *userptr) ? userptr[1] : '?';
4273     int y, x;
4274
4275     wmove(win, 1, 1);
4276     wprintw(win, "-pan%c-", num);
4277     wclrtoeol(win);
4278     box(win, 0, 0);
4279     for (y = 2; y < getmaxy(win) - 1; y++) {
4280         for (x = 1; x < getmaxx(win) - 1; x++) {
4281             wmove(win, y, x);
4282             waddch(win, UChar(num));
4283         }
4284     }
4285 }
4286
4287 #if USE_WIDEC_SUPPORT
4288 static void
4289 init_wide_panel(void)
4290 {
4291     int digit;
4292     cchar_t temp[10];
4293
4294     for (digit = 0; digit < 10; ++digit)
4295         make_fullwidth_digit(&temp[digit], digit);
4296
4297     do {
4298         int y, x;
4299         getyx(stdscr, y, x);
4300         digit = (y + x / 2) % 10;
4301     } while (add_wch(&temp[digit]) != ERR);
4302 }
4303
4304 static void
4305 fill_wide_panel(PANEL * pan)
4306 {
4307     WINDOW *win = panel_window(pan);
4308     const char *userptr = (const char *) panel_userptr(pan);
4309     int num = (userptr && *userptr) ? userptr[1] : '?';
4310     int y, x;
4311
4312     wmove(win, 1, 1);
4313     wprintw(win, "-pan%c-", num);
4314     wclrtoeol(win);
4315     box(win, 0, 0);
4316     for (y = 2; y < getmaxy(win) - 1; y++) {
4317         for (x = 1; x < getmaxx(win) - 1; x++) {
4318             wmove(win, y, x);
4319             waddch(win, UChar(num));
4320         }
4321     }
4322 }
4323 #endif
4324
4325 #define MAX_PANELS 5
4326
4327 static void
4328 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4329 {
4330     int which = cmd[1] - '0';
4331
4332     saywhat(cmd);
4333     switch (*cmd) {
4334     case 'h':
4335         hide_panel(px[which]);
4336         break;
4337     case 's':
4338         show_panel(px[which]);
4339         break;
4340     case 't':
4341         top_panel(px[which]);
4342         break;
4343     case 'b':
4344         bottom_panel(px[which]);
4345         break;
4346     case 'd':
4347         rmpanel(px[which]);
4348         break;
4349     }
4350     pflush();
4351     wait_a_while(nap_msec);
4352 }
4353
4354 static void
4355 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4356 {
4357     int count;
4358     int itmp;
4359     PANEL *px[MAX_PANELS + 1];
4360
4361     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4362     refresh();
4363
4364     InitPanel();
4365     for (count = 0; count < 5; count++) {
4366         px[1] = mkpanel(COLOR_RED,
4367                         LINES / 2 - 2,
4368                         COLS / 8 + 1,
4369                         0,
4370                         0);
4371         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4372
4373         px[2] = mkpanel(COLOR_GREEN,
4374                         LINES / 2 + 1,
4375                         COLS / 7,
4376                         LINES / 4,
4377                         COLS / 10);
4378         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4379
4380         px[3] = mkpanel(COLOR_YELLOW,
4381                         LINES / 4,
4382                         COLS / 10,
4383                         LINES / 2,
4384                         COLS / 9);
4385         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4386
4387         px[4] = mkpanel(COLOR_BLUE,
4388                         LINES / 2 - 2,
4389                         COLS / 8,
4390                         LINES / 2 - 2,
4391                         COLS / 3);
4392         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4393
4394         px[5] = mkpanel(COLOR_MAGENTA,
4395                         LINES / 2 - 2,
4396                         COLS / 8,
4397                         LINES / 2,
4398                         COLS / 2 - 2);
4399         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4400
4401         FillPanel(px[1]);
4402         FillPanel(px[2]);
4403         FillPanel(px[3]);
4404         FillPanel(px[4]);
4405         FillPanel(px[5]);
4406
4407         hide_panel(px[4]);
4408         hide_panel(px[5]);
4409         pflush();
4410         saywhat("");
4411         wait_a_while(nap_msec);
4412
4413         saywhat("h3 s1 s2 s4 s5");
4414         move_panel(px[1], 0, 0);
4415         hide_panel(px[3]);
4416         show_panel(px[1]);
4417         show_panel(px[2]);
4418         show_panel(px[4]);
4419         show_panel(px[5]);
4420         pflush();
4421         wait_a_while(nap_msec);
4422
4423         canned_panel(px, "s1");
4424         canned_panel(px, "s2");
4425
4426         saywhat("m2");
4427         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4428         pflush();
4429         wait_a_while(nap_msec);
4430
4431         canned_panel(px, "s3");
4432
4433         saywhat("m3");
4434         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4435         pflush();
4436         wait_a_while(nap_msec);
4437
4438         canned_panel(px, "b3");
4439         canned_panel(px, "s4");
4440         canned_panel(px, "s5");
4441         canned_panel(px, "t3");
4442         canned_panel(px, "t1");
4443         canned_panel(px, "t2");
4444         canned_panel(px, "t3");
4445         canned_panel(px, "t4");
4446
4447         for (itmp = 0; itmp < 6; itmp++) {
4448             WINDOW *w4 = panel_window(px[4]);
4449             WINDOW *w5 = panel_window(px[5]);
4450
4451             saywhat("m4");
4452             wmove(w4, LINES / 8, 1);
4453             waddstr(w4, mod[itmp]);
4454             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4455             wmove(w5, LINES / 6, 1);
4456             waddstr(w5, mod[itmp]);
4457             pflush();
4458             wait_a_while(nap_msec);
4459
4460             saywhat("m5");
4461             wmove(w4, LINES / 6, 1);
4462             waddstr(w4, mod[itmp]);
4463             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4464             wmove(w5, LINES / 8, 1);
4465             waddstr(w5, mod[itmp]);
4466             pflush();
4467             wait_a_while(nap_msec);
4468         }
4469
4470         saywhat("m4");
4471         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4472         pflush();
4473         wait_a_while(nap_msec);
4474
4475         canned_panel(px, "t5");
4476         canned_panel(px, "t2");
4477         canned_panel(px, "t1");
4478         canned_panel(px, "d2");
4479         canned_panel(px, "h3");
4480         canned_panel(px, "d1");
4481         canned_panel(px, "d4");
4482         canned_panel(px, "d5");
4483         canned_panel(px, "d3");
4484
4485         wait_a_while(nap_msec);
4486         if (nap_msec == 1)
4487             break;
4488         nap_msec = 100L;
4489     }
4490
4491     erase();
4492     endwin();
4493 }
4494 #endif /* USE_LIBPANEL */
4495
4496 /****************************************************************************
4497  *
4498  * Pad tester
4499  *
4500  ****************************************************************************/
4501
4502 #define GRIDSIZE        3
4503
4504 static bool pending_pan = FALSE;
4505 static bool show_panner_legend = TRUE;
4506
4507 static int
4508 panner_legend(int line)
4509 {
4510     static const char *const legend[] =
4511     {
4512         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4513         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4514         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4515         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4516     };
4517     int n = ((int) SIZEOF(legend) - (LINES - line));
4518     if (line < LINES && (n >= 0)) {
4519         move(line, 0);
4520         if (show_panner_legend)
4521             printw("%s", legend[n]);
4522         clrtoeol();
4523         return show_panner_legend;
4524     }
4525     return FALSE;
4526 }
4527
4528 static void
4529 panner_h_cleanup(int from_y, int from_x, int to_x)
4530 {
4531     if (!panner_legend(from_y))
4532         do_h_line(from_y, from_x, ' ', to_x);
4533 }
4534
4535 static void
4536 panner_v_cleanup(int from_y, int from_x, int to_y)
4537 {
4538     if (!panner_legend(from_y))
4539         do_v_line(from_y, from_x, ' ', to_y);
4540 }
4541
4542 static void
4543 fill_pad(WINDOW *panpad, bool pan_lines)
4544 {
4545     int y, x;
4546     unsigned gridcount = 0;
4547
4548     wmove(panpad, 0, 0);
4549     for (y = 0; y < getmaxy(panpad); y++) {
4550         for (x = 0; x < getmaxx(panpad); x++) {
4551             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4552                 if (y == 0 && x == 0)
4553                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4554                 else if (y == 0)
4555                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4556                 else if (y == 0 || x == 0)
4557                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4558                 else
4559                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4560                                              (int) (gridcount++ % 26)));
4561             } else if (y % GRIDSIZE == 0)
4562                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4563             else if (x % GRIDSIZE == 0)
4564                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4565             else
4566                 waddch(panpad, ' ');
4567         }
4568     }
4569 }
4570
4571 static void
4572 panner(WINDOW *pad,
4573        int top_x, int top_y, int porty, int portx,
4574        int (*pgetc) (WINDOW *))
4575 {
4576 #if HAVE_GETTIMEOFDAY
4577     struct timeval before, after;
4578     bool timing = TRUE;
4579 #endif
4580     bool pan_lines = FALSE;
4581     bool scrollers = TRUE;
4582     int basex = 0;
4583     int basey = 0;
4584     int pxmax, pymax, lowend, highend, c;
4585
4586     getmaxyx(pad, pymax, pxmax);
4587     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4588
4589     c = KEY_REFRESH;
4590     do {
4591 #ifdef NCURSES_VERSION
4592         /*
4593          * During shell-out, the user may have resized the window.  Adjust
4594          * the port size of the pad to accommodate this.  Ncurses automatically
4595          * resizes all of the normal windows to fit on the new screen.
4596          */
4597         if (top_x > COLS)
4598             top_x = COLS;
4599         if (portx > COLS)
4600             portx = COLS;
4601         if (top_y > LINES)
4602             top_y = LINES;
4603         if (porty > LINES)
4604             porty = LINES;
4605 #endif
4606         switch (c) {
4607         case KEY_REFRESH:
4608             erase();
4609
4610             /* FALLTHRU */
4611         case '?':
4612             if (c == '?')
4613                 show_panner_legend = !show_panner_legend;
4614             panner_legend(LINES - 4);
4615             panner_legend(LINES - 3);
4616             panner_legend(LINES - 2);
4617             panner_legend(LINES - 1);
4618             break;
4619         case 'a':
4620             pan_lines = !pan_lines;
4621             fill_pad(pad, pan_lines);
4622             pending_pan = FALSE;
4623             break;
4624
4625 #if HAVE_GETTIMEOFDAY
4626         case 't':
4627             timing = !timing;
4628             if (!timing)
4629                 panner_legend(LINES - 1);
4630             break;
4631 #endif
4632         case 's':
4633             scrollers = !scrollers;
4634             break;
4635
4636             /* Move the top-left corner of the pad, keeping the bottom-right
4637              * corner fixed.
4638              */
4639         case 'h':               /* increase-columns: move left edge to left */
4640             if (top_x <= 0)
4641                 beep();
4642             else {
4643                 panner_v_cleanup(top_y, top_x, porty);
4644                 top_x--;
4645             }
4646             break;
4647
4648         case 'j':               /* decrease-lines: move top-edge down */
4649             if (top_y >= porty)
4650                 beep();
4651             else {
4652                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4653                 top_y++;
4654             }
4655             break;
4656
4657         case 'k':               /* increase-lines: move top-edge up */
4658             if (top_y <= 0)
4659                 beep();
4660             else {
4661                 top_y--;
4662                 panner_h_cleanup(top_y, top_x, portx);
4663             }
4664             break;
4665
4666         case 'l':               /* decrease-columns: move left-edge to right */
4667             if (top_x >= portx)
4668                 beep();
4669             else {
4670                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4671                 top_x++;
4672             }
4673             break;
4674
4675             /* Move the bottom-right corner of the pad, keeping the top-left
4676              * corner fixed.
4677              */
4678         case KEY_IC:            /* increase-columns: move right-edge to right */
4679             if (portx >= pxmax || portx >= COLS)
4680                 beep();
4681             else {
4682                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4683                 ++portx;
4684             }
4685             break;
4686
4687         case KEY_IL:            /* increase-lines: move bottom-edge down */
4688             if (porty >= pymax || porty >= LINES)
4689                 beep();
4690             else {
4691                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4692                 ++porty;
4693             }
4694             break;
4695
4696         case KEY_DC:            /* decrease-columns: move bottom edge up */
4697             if (portx <= top_x)
4698                 beep();
4699             else {
4700                 portx--;
4701                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4702             }
4703             break;
4704
4705         case KEY_DL:            /* decrease-lines */
4706             if (porty <= top_y)
4707                 beep();
4708             else {
4709                 porty--;
4710                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4711             }
4712             break;
4713
4714         case KEY_LEFT:          /* pan leftwards */
4715             if (basex > 0)
4716                 basex--;
4717             else
4718                 beep();
4719             break;
4720
4721         case KEY_RIGHT: /* pan rightwards */
4722             if (basex + portx - (pymax > porty) < pxmax)
4723                 basex++;
4724             else
4725                 beep();
4726             break;
4727
4728         case KEY_UP:            /* pan upwards */
4729             if (basey > 0)
4730                 basey--;
4731             else
4732                 beep();
4733             break;
4734
4735         case KEY_DOWN:          /* pan downwards */
4736             if (basey + porty - (pxmax > portx) < pymax)
4737                 basey++;
4738             else
4739                 beep();
4740             break;
4741
4742         case 'H':
4743         case KEY_HOME:
4744         case KEY_FIND:
4745             basey = 0;
4746             break;
4747
4748         case 'E':
4749         case KEY_END:
4750         case KEY_SELECT:
4751             basey = pymax - porty;
4752             if (basey < 0)
4753                 basey = 0;
4754             break;
4755
4756         default:
4757             beep();
4758             break;
4759         }
4760
4761         mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
4762         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
4763         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
4764
4765         if (scrollers && (pxmax > portx - 1)) {
4766             int length = (portx - top_x - 1);
4767             float ratio = ((float) length) / ((float) pxmax);
4768
4769             lowend = (int) ((float) top_x + ((float) basex * ratio));
4770             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
4771
4772             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
4773             if (highend < portx) {
4774                 attron(A_REVERSE);
4775                 do_h_line(porty - 1, lowend, ' ', highend + 1);
4776                 attroff(A_REVERSE);
4777                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
4778             }
4779         } else
4780             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
4781
4782         if (scrollers && (pymax > porty - 1)) {
4783             int length = (porty - top_y - 1);
4784             float ratio = ((float) length) / ((float) pymax);
4785
4786             lowend = (int) ((float) top_y + ((float) basey * ratio));
4787             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
4788
4789             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
4790             if (highend < porty) {
4791                 attron(A_REVERSE);
4792                 do_v_line(lowend, portx - 1, ' ', highend + 1);
4793                 attroff(A_REVERSE);
4794                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
4795             }
4796         } else
4797             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
4798
4799         mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
4800         mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
4801         mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
4802
4803         if (!pending_pan) {
4804 #if HAVE_GETTIMEOFDAY
4805             gettimeofday(&before, 0);
4806 #endif
4807             wnoutrefresh(stdscr);
4808
4809             pnoutrefresh(pad,
4810                          basey, basex,
4811                          top_y, top_x,
4812                          porty - (pxmax > portx) - 1,
4813                          portx - (pymax > porty) - 1);
4814
4815             doupdate();
4816 #if HAVE_GETTIMEOFDAY
4817             if (timing) {
4818                 double elapsed;
4819                 gettimeofday(&after, 0);
4820                 elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
4821                     - (before.tv_sec + before.tv_usec / 1.0e6);
4822                 move(LINES - 1, COLS - 12);
4823                 printw("Secs: %2.03f", elapsed);
4824                 refresh();
4825             }
4826 #endif
4827         }
4828
4829     } while
4830         ((c = pgetc(pad)) != KEY_EXIT);
4831
4832     scrollok(stdscr, TRUE);     /* reset to driver's default */
4833 }
4834
4835 static int
4836 padgetch(WINDOW *win)
4837 {
4838     static int count;
4839     static int last;
4840     int c;
4841
4842     if ((pending_pan = (count > 0)) != FALSE) {
4843         count--;
4844         pending_pan = (count != 0);
4845     } else {
4846         for (;;) {
4847             switch (c = wGetchar(win)) {
4848             case '!':
4849                 ShellOut(FALSE);
4850                 /* FALLTHRU */
4851             case CTRL('r'):
4852                 endwin();
4853                 refresh();
4854                 c = KEY_REFRESH;
4855                 break;
4856             case CTRL('l'):
4857                 c = KEY_REFRESH;
4858                 break;
4859             case 'U':
4860                 c = KEY_UP;
4861                 break;
4862             case 'D':
4863                 c = KEY_DOWN;
4864                 break;
4865             case 'R':
4866                 c = KEY_RIGHT;
4867                 break;
4868             case 'L':
4869                 c = KEY_LEFT;
4870                 break;
4871             case '+':
4872                 c = KEY_IL;
4873                 break;
4874             case '-':
4875                 c = KEY_DL;
4876                 break;
4877             case '>':
4878                 c = KEY_IC;
4879                 break;
4880             case '<':
4881                 c = KEY_DC;
4882                 break;
4883             case ERR:           /* FALLTHRU */
4884             case case_QUIT:
4885                 count = 0;
4886                 c = KEY_EXIT;
4887                 break;
4888             default:
4889                 if (c >= '0' && c <= '9') {
4890                     count = count * 10 + (c - '0');
4891                     continue;
4892                 }
4893                 break;
4894             }
4895             last = c;
4896             break;
4897         }
4898         if (count > 0)
4899             count--;
4900     }
4901     return (last);
4902 }
4903
4904 #define PAD_HIGH 200
4905 #define PAD_WIDE 200
4906
4907 static void
4908 demo_pad(void)
4909 /* Demonstrate pads. */
4910 {
4911     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
4912
4913     if (panpad == 0) {
4914         Cannot("cannot create requested pad");
4915         return;
4916     }
4917
4918     fill_pad(panpad, FALSE);
4919
4920     panner_legend(LINES - 4);
4921     panner_legend(LINES - 3);
4922     panner_legend(LINES - 2);
4923     panner_legend(LINES - 1);
4924
4925     keypad(panpad, TRUE);
4926
4927     /* Make the pad (initially) narrow enough that a trace file won't wrap.
4928      * We'll still be able to widen it during a test, since that's required
4929      * for testing boundaries.
4930      */
4931     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
4932
4933     delwin(panpad);
4934     endwin();
4935     erase();
4936 }
4937
4938 /****************************************************************************
4939  *
4940  * Tests from John Burnell's PDCurses tester
4941  *
4942  ****************************************************************************/
4943
4944 static void
4945 Continue(WINDOW *win)
4946 {
4947     noecho();
4948     wmove(win, 10, 1);
4949     mvwaddstr(win, 10, 1, " Press any key to continue");
4950     wrefresh(win);
4951     wGetchar(win);
4952 }
4953
4954 static void
4955 flushinp_test(WINDOW *win)
4956 /* Input test, adapted from John Burnell's PDCurses tester */
4957 {
4958     int w, h, bx, by, sw, sh, i;
4959
4960     WINDOW *subWin;
4961     wclear(win);
4962
4963     getmaxyx(win, h, w);
4964     getbegyx(win, by, bx);
4965     sw = w / 3;
4966     sh = h / 3;
4967     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
4968         return;
4969
4970 #ifdef A_COLOR
4971     if (use_colors) {
4972         init_pair(2, COLOR_CYAN, COLOR_BLUE);
4973         wbkgd(subWin, COLOR_PAIR(2) | ' ');
4974     }
4975 #endif
4976     wattrset(subWin, A_BOLD);
4977     box(subWin, ACS_VLINE, ACS_HLINE);
4978     mvwaddstr(subWin, 2, 1, "This is a subwindow");
4979     wrefresh(win);
4980
4981     /*
4982      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
4983      * it only happened to "work" on SVr4 because that implementation does not
4984      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
4985      * test behavior, we're using 'cbreak()', which will allow a single
4986      * character to return without needing a newline. - T.Dickey 1997/10/11.
4987      */
4988     cbreak();
4989     mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
4990
4991     mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
4992     mvwaddstr(win, 3, 1,
4993               "These should be discarded (not echoed) after the subwindow goes away.");
4994     wrefresh(win);
4995
4996     for (i = 0; i < 5; i++) {
4997         mvwprintw(subWin, 1, 1, "Time = %d", i);
4998         wrefresh(subWin);
4999         napms(1000);
5000         flushinp();
5001     }
5002
5003     delwin(subWin);
5004     werase(win);
5005     flash();
5006     wrefresh(win);
5007     napms(1000);
5008
5009     mvwaddstr(win, 2, 1,
5010               "If you were still typing when the window timer expired,");
5011     mvwaddstr(win, 3, 1,
5012               "or else you typed nothing at all while it was running,");
5013     mvwaddstr(win, 4, 1,
5014               "test was invalid.  You'll see garbage or nothing at all. ");
5015     mvwaddstr(win, 6, 1, "Press a key");
5016     wmove(win, 9, 10);
5017     wrefresh(win);
5018     echo();
5019     wGetchar(win);
5020     flushinp();
5021     mvwaddstr(win, 12, 0,
5022               "If you see any key other than what you typed, flushinp() is broken.");
5023     Continue(win);
5024
5025     wmove(win, 9, 10);
5026     wdelch(win);
5027     wrefresh(win);
5028     wmove(win, 12, 0);
5029     clrtoeol();
5030     waddstr(win,
5031             "What you typed should now have been deleted; if not, wdelch() failed.");
5032     Continue(win);
5033
5034     cbreak();
5035 }
5036
5037 /****************************************************************************
5038  *
5039  * Menu test
5040  *
5041  ****************************************************************************/
5042
5043 #if USE_LIBMENU
5044
5045 #define MENU_Y  8
5046 #define MENU_X  8
5047
5048 static int
5049 menu_virtualize(int c)
5050 {
5051     if (c == '\n' || c == KEY_EXIT)
5052         return (MAX_COMMAND + 1);
5053     else if (c == 'u')
5054         return (REQ_SCR_ULINE);
5055     else if (c == 'd')
5056         return (REQ_SCR_DLINE);
5057     else if (c == 'b' || c == KEY_NPAGE)
5058         return (REQ_SCR_UPAGE);
5059     else if (c == 'f' || c == KEY_PPAGE)
5060         return (REQ_SCR_DPAGE);
5061     else if (c == 'n' || c == KEY_DOWN)
5062         return (REQ_NEXT_ITEM);
5063     else if (c == 'p' || c == KEY_UP)
5064         return (REQ_PREV_ITEM);
5065     else if (c == ' ')
5066         return (REQ_TOGGLE_ITEM);
5067     else {
5068         if (c != KEY_MOUSE)
5069             beep();
5070         return (c);
5071     }
5072 }
5073
5074 static CONST_MENUS char *animals[] =
5075 {
5076     "Lions",
5077     "Tigers",
5078     "Bears",
5079     "(Oh my!)",
5080     "Newts",
5081     "Platypi",
5082     "Lemurs",
5083     "(Oh really?!)",
5084     "Leopards",
5085     "Panthers",
5086     "Pumas",
5087     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5088     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5089     (char *) 0
5090 };
5091
5092 static void
5093 menu_test(void)
5094 {
5095     MENU *m;
5096     ITEM *items[SIZEOF(animals)];
5097     ITEM **ip = items;
5098     CONST_MENUS char **ap;
5099     int mrows, mcols, c;
5100     WINDOW *menuwin;
5101
5102 #ifdef NCURSES_MOUSE_VERSION
5103     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5104 #endif
5105     mvaddstr(0, 0, "This is the menu test:");
5106     mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
5107     mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
5108     mvaddstr(4, 0,
5109              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5110     mvaddstr(5, 0, "  Press return to exit.");
5111     refresh();
5112
5113     for (ap = animals; *ap; ap++) {
5114         if ((*ip = new_item(*ap, "")) != 0)
5115             ++ip;
5116     }
5117     *ip = (ITEM *) 0;
5118
5119     m = new_menu(items);
5120
5121     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5122     scale_menu(m, &mrows, &mcols);
5123
5124     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5125     set_menu_win(m, menuwin);
5126     keypad(menuwin, TRUE);
5127     box(menuwin, 0, 0);
5128
5129     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5130
5131     post_menu(m);
5132
5133     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5134         if (c == E_NOT_POSTED)
5135             break;
5136         if (c == E_REQUEST_DENIED)
5137             beep();
5138         continue;
5139     }
5140
5141     (void) mvprintw(LINES - 2, 0,
5142                     "You chose: %s\n", item_name(current_item(m)));
5143     (void) addstr("Press any key to continue...");
5144     wGetchar(stdscr);
5145
5146     unpost_menu(m);
5147     delwin(menuwin);
5148
5149     free_menu(m);
5150     for (ip = items; *ip; ip++)
5151         free_item(*ip);
5152 #ifdef NCURSES_MOUSE_VERSION
5153     mousemask(0, (mmask_t *) 0);
5154 #endif
5155 }
5156
5157 #ifdef TRACE
5158 #define T_TBL(name) { #name, name }
5159 static struct {
5160     const char *name;
5161     unsigned mask;
5162 } t_tbl[] = {
5163
5164     T_TBL(TRACE_DISABLE),
5165         T_TBL(TRACE_TIMES),
5166         T_TBL(TRACE_TPUTS),
5167         T_TBL(TRACE_UPDATE),
5168         T_TBL(TRACE_MOVE),
5169         T_TBL(TRACE_CHARPUT),
5170         T_TBL(TRACE_ORDINARY),
5171         T_TBL(TRACE_CALLS),
5172         T_TBL(TRACE_VIRTPUT),
5173         T_TBL(TRACE_IEVENT),
5174         T_TBL(TRACE_BITS),
5175         T_TBL(TRACE_ICALLS),
5176         T_TBL(TRACE_CCALLS),
5177         T_TBL(TRACE_DATABASE),
5178         T_TBL(TRACE_ATTRS),
5179         T_TBL(TRACE_MAXIMUM),
5180     {
5181         (char *) 0, 0
5182     }
5183 };
5184
5185 static char *
5186 tracetrace(unsigned tlevel)
5187 {
5188     static char *buf;
5189     int n;
5190
5191     if (buf == 0) {
5192         size_t need = 12;
5193         for (n = 0; t_tbl[n].name != 0; n++)
5194             need += strlen(t_tbl[n].name) + 2;
5195         buf = typeMalloc(char, need);
5196     }
5197     sprintf(buf, "0x%02x = {", tlevel);
5198     if (tlevel == 0) {
5199         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
5200     } else {
5201         for (n = 1; t_tbl[n].name != 0; n++)
5202             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5203                 strcat(buf, t_tbl[n].name);
5204                 strcat(buf, ", ");
5205             }
5206     }
5207     if (buf[strlen(buf) - 2] == ',')
5208         buf[strlen(buf) - 2] = '\0';
5209     return (strcat(buf, "}"));
5210 }
5211
5212 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5213  * the others
5214  */
5215 static int
5216 run_trace_menu(MENU * m)
5217 {
5218     ITEM **items;
5219     ITEM *i, **p;
5220
5221     for (;;) {
5222         bool changed = FALSE;
5223         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5224         case E_UNKNOWN_COMMAND:
5225             return FALSE;
5226         default:
5227             items = menu_items(m);
5228             i = current_item(m);
5229             if (i == items[0]) {
5230                 if (item_value(i)) {
5231                     for (p = items + 1; *p != 0; p++)
5232                         if (item_value(*p)) {
5233                             set_item_value(*p, FALSE);
5234                             changed = TRUE;
5235                         }
5236                 }
5237             } else {
5238                 for (p = items + 1; *p != 0; p++)
5239                     if (item_value(*p)) {
5240                         set_item_value(items[0], FALSE);
5241                         changed = TRUE;
5242                         break;
5243                     }
5244             }
5245             if (!changed)
5246                 return TRUE;
5247         }
5248     }
5249 }
5250
5251 static void
5252 trace_set(void)
5253 /* interactively set the trace level */
5254 {
5255     MENU *m;
5256     ITEM *items[SIZEOF(t_tbl)];
5257     ITEM **ip = items;
5258     int mrows, mcols;
5259     unsigned newtrace;
5260     int n;
5261     WINDOW *menuwin;
5262
5263     mvaddstr(0, 0, "Interactively set trace level:");
5264     mvaddstr(2, 0, "  Press space bar to toggle a selection.");
5265     mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
5266     mvaddstr(4, 0, "  Press return to set the trace level.");
5267     mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5268
5269     refresh();
5270
5271     for (n = 0; t_tbl[n].name != 0; n++) {
5272         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
5273             ++ip;
5274         }
5275     }
5276     *ip = (ITEM *) 0;
5277
5278     m = new_menu(items);
5279
5280     set_menu_format(m, 0, 2);
5281     scale_menu(m, &mrows, &mcols);
5282
5283     menu_opts_off(m, O_ONEVALUE);
5284     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5285     set_menu_win(m, menuwin);
5286     keypad(menuwin, TRUE);
5287     box(menuwin, 0, 0);
5288
5289     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5290
5291     post_menu(m);
5292
5293     for (ip = menu_items(m); *ip; ip++) {
5294         unsigned mask = t_tbl[item_index(*ip)].mask;
5295         if (mask == 0)
5296             set_item_value(*ip, _nc_tracing == 0);
5297         else if ((mask & _nc_tracing) == mask)
5298             set_item_value(*ip, TRUE);
5299     }
5300
5301     while (run_trace_menu(m))
5302         continue;
5303
5304     newtrace = 0;
5305     for (ip = menu_items(m); *ip; ip++)
5306         if (item_value(*ip))
5307             newtrace |= t_tbl[item_index(*ip)].mask;
5308     trace(newtrace);
5309     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5310
5311     (void) mvprintw(LINES - 2, 0,
5312                     "Trace level is %s\n", tracetrace(_nc_tracing));
5313     (void) addstr("Press any key to continue...");
5314     wGetchar(stdscr);
5315
5316     unpost_menu(m);
5317     delwin(menuwin);
5318
5319     free_menu(m);
5320     for (ip = items; *ip; ip++)
5321         free_item(*ip);
5322 }
5323 #endif /* TRACE */
5324 #endif /* USE_LIBMENU */
5325
5326 /****************************************************************************
5327  *
5328  * Forms test
5329  *
5330  ****************************************************************************/
5331 #if USE_LIBFORM
5332 static FIELD *
5333 make_label(int frow, int fcol, NCURSES_CONST char *label)
5334 {
5335     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5336
5337     if (f) {
5338         set_field_buffer(f, 0, label);
5339         set_field_opts(f, (int) (field_opts(f) & ~O_ACTIVE));
5340     }
5341     return (f);
5342 }
5343
5344 static FIELD *
5345 make_field(int frow, int fcol, int rows, int cols, bool secure)
5346 {
5347     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5348
5349     if (f) {
5350         set_field_back(f, A_UNDERLINE);
5351         set_field_userptr(f, (void *) 0);
5352     }
5353     return (f);
5354 }
5355
5356 static void
5357 display_form(FORM * f)
5358 {
5359     WINDOW *w;
5360     int rows, cols;
5361
5362     scale_form(f, &rows, &cols);
5363
5364     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5365         set_form_win(f, w);
5366         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5367         box(w, 0, 0);
5368         keypad(w, TRUE);
5369     }
5370
5371     if (post_form(f) != E_OK)
5372         wrefresh(w);
5373 }
5374
5375 static void
5376 erase_form(FORM * f)
5377 {
5378     WINDOW *w = form_win(f);
5379     WINDOW *s = form_sub(f);
5380
5381     unpost_form(f);
5382     werase(w);
5383     wrefresh(w);
5384     delwin(s);
5385     delwin(w);
5386 }
5387
5388 static int
5389 edit_secure(FIELD * me, int c)
5390 {
5391     int rows, cols, frow, fcol, nrow, nbuf;
5392
5393     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5394         && nbuf > 0) {
5395         char *source = field_buffer(me, 1);
5396         char temp[80];
5397         long len;
5398
5399         strcpy(temp, source ? source : "");
5400         len = (long) (char *) field_userptr(me);
5401         if (c <= KEY_MAX) {
5402             if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5403                 temp[len++] = (char) c;
5404                 temp[len] = 0;
5405                 set_field_buffer(me, 1, temp);
5406                 c = '*';
5407             } else {
5408                 c = 0;
5409             }
5410         } else {
5411             switch (c) {
5412             case REQ_BEG_FIELD:
5413             case REQ_CLR_EOF:
5414             case REQ_CLR_EOL:
5415             case REQ_DEL_LINE:
5416             case REQ_DEL_WORD:
5417             case REQ_DOWN_CHAR:
5418             case REQ_END_FIELD:
5419             case REQ_INS_CHAR:
5420             case REQ_INS_LINE:
5421             case REQ_LEFT_CHAR:
5422             case REQ_NEW_LINE:
5423             case REQ_NEXT_WORD:
5424             case REQ_PREV_WORD:
5425             case REQ_RIGHT_CHAR:
5426             case REQ_UP_CHAR:
5427                 c = 0;          /* we don't want to do inline editing */
5428                 break;
5429             case REQ_CLR_FIELD:
5430                 if (len) {
5431                     temp[0] = 0;
5432                     set_field_buffer(me, 1, temp);
5433                 }
5434                 break;
5435             case REQ_DEL_CHAR:
5436             case REQ_DEL_PREV:
5437                 if (len) {
5438                     temp[--len] = 0;
5439                     set_field_buffer(me, 1, temp);
5440                 }
5441                 break;
5442             }
5443         }
5444         set_field_userptr(me, (void *) len);
5445     }
5446     return c;
5447 }
5448
5449 static int
5450 form_virtualize(FORM * f, WINDOW *w)
5451 {
5452     /* *INDENT-OFF* */
5453     static const struct {
5454         int code;
5455         int result;
5456     } lookup[] = {
5457         { CTRL('A'),    REQ_NEXT_CHOICE },
5458         { CTRL('B'),    REQ_PREV_WORD },
5459         { CTRL('C'),    REQ_CLR_EOL },
5460         { CTRL('D'),    REQ_DOWN_FIELD },
5461         { CTRL('E'),    REQ_END_FIELD },
5462         { CTRL('F'),    REQ_NEXT_PAGE },
5463         { CTRL('G'),    REQ_DEL_WORD },
5464         { CTRL('H'),    REQ_DEL_PREV },
5465         { CTRL('I'),    REQ_INS_CHAR },
5466         { CTRL('K'),    REQ_CLR_EOF },
5467         { CTRL('L'),    REQ_LEFT_FIELD },
5468         { CTRL('M'),    REQ_NEW_LINE },
5469         { CTRL('N'),    REQ_NEXT_FIELD },
5470         { CTRL('O'),    REQ_INS_LINE },
5471         { CTRL('P'),    REQ_PREV_FIELD },
5472         { CTRL('R'),    REQ_RIGHT_FIELD },
5473         { CTRL('S'),    REQ_BEG_FIELD },
5474         { CTRL('U'),    REQ_UP_FIELD },
5475         { CTRL('V'),    REQ_DEL_CHAR },
5476         { CTRL('W'),    REQ_NEXT_WORD },
5477         { CTRL('X'),    REQ_CLR_FIELD },
5478         { CTRL('Y'),    REQ_DEL_LINE },
5479         { CTRL('Z'),    REQ_PREV_CHOICE },
5480         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5481         { KEY_BACKSPACE, REQ_DEL_PREV },
5482         { KEY_DOWN,     REQ_DOWN_CHAR },
5483         { KEY_END,      REQ_LAST_FIELD },
5484         { KEY_HOME,     REQ_FIRST_FIELD },
5485         { KEY_LEFT,     REQ_LEFT_CHAR },
5486         { KEY_LL,       REQ_LAST_FIELD },
5487         { KEY_NEXT,     REQ_NEXT_FIELD },
5488         { KEY_NPAGE,    REQ_NEXT_PAGE },
5489         { KEY_PPAGE,    REQ_PREV_PAGE },
5490         { KEY_PREVIOUS, REQ_PREV_FIELD },
5491         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5492         { KEY_UP,       REQ_UP_CHAR },
5493         { QUIT,         MAX_FORM_COMMAND + 1 }
5494     };
5495     /* *INDENT-ON* */
5496
5497     static int mode = REQ_INS_MODE;
5498     int c = wGetchar(w);
5499     unsigned n;
5500     FIELD *me = current_field(f);
5501     bool current = TRUE;
5502
5503     if (c == CTRL(']')) {
5504         if (mode == REQ_INS_MODE) {
5505             mode = REQ_OVL_MODE;
5506         } else {
5507             mode = REQ_INS_MODE;
5508         }
5509         c = mode;
5510     } else {
5511         for (n = 0; n < SIZEOF(lookup); n++) {
5512             if (lookup[n].code == c) {
5513                 c = lookup[n].result;
5514                 break;
5515             }
5516         }
5517     }
5518     mvprintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5519
5520     /*
5521      * Force the field that the user is typing into to be in reverse video,
5522      * while the other fields are shown underlined.
5523      */
5524     switch (c) {
5525     case REQ_BEG_FIELD:
5526     case REQ_CLR_EOF:
5527     case REQ_CLR_EOL:
5528     case REQ_CLR_FIELD:
5529     case REQ_DEL_CHAR:
5530     case REQ_DEL_LINE:
5531     case REQ_DEL_PREV:
5532     case REQ_DEL_WORD:
5533     case REQ_END_FIELD:
5534     case REQ_INS_CHAR:
5535     case REQ_INS_LINE:
5536     case REQ_LEFT_CHAR:
5537     case REQ_LEFT_FIELD:
5538     case REQ_NEXT_WORD:
5539     case REQ_RIGHT_CHAR:
5540         current = TRUE;
5541         break;
5542     default:
5543         current = (c < KEY_MAX);
5544         break;
5545     }
5546     if (current) {
5547         c = edit_secure(me, c);
5548         set_field_back(me, A_REVERSE);
5549     } else {
5550         c = edit_secure(me, c);
5551         set_field_back(me, A_UNDERLINE);
5552     }
5553     return c;
5554 }
5555
5556 static int
5557 my_form_driver(FORM * form, int c)
5558 {
5559     if (c == (MAX_FORM_COMMAND + 1)
5560         && form_driver(form, REQ_VALIDATION) == E_OK)
5561         return (TRUE);
5562     else {
5563         beep();
5564         return (FALSE);
5565     }
5566 }
5567
5568 #ifdef NCURSES_VERSION
5569 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5570 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5571 #else
5572 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5573 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5574 #endif
5575
5576 /*
5577  * Allow a middle initial, optionally with a '.' to end it.
5578  */
5579 static
5580 FIELDCHECK_CB(mi_field_check)
5581 {
5582     char *s = field_buffer(fld, 0);
5583     int state = 0;
5584     int n;
5585
5586     for (n = 0; s[n] != '\0'; ++n) {
5587         switch (state) {
5588         case 0:
5589             if (s[n] == '.') {
5590                 if (n != 1)
5591                     return FALSE;
5592                 state = 2;
5593             } else if (isspace(UChar(s[n]))) {
5594                 state = 2;
5595             }
5596             break;
5597         case 2:
5598             if (!isspace(UChar(s[n])))
5599                 return FALSE;
5600             break;
5601         }
5602     }
5603
5604     /* force the form to display a leading capital */
5605     if (islower(UChar(s[0]))) {
5606         s[0] = (char) toupper(UChar(s[0]));
5607         set_field_buffer(fld, 0, s);
5608     }
5609     return TRUE;
5610 }
5611
5612 static
5613 CHAR_CHECK_CB(mi_char_check)
5614 {
5615     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
5616 }
5617
5618 /*
5619  * Passwords should be at least 6 characters.
5620  */
5621 static
5622 FIELDCHECK_CB(pw_field_check)
5623 {
5624     char *s = field_buffer(fld, 0);
5625     int n;
5626
5627     for (n = 0; s[n] != '\0'; ++n) {
5628         if (isspace(UChar(s[n]))) {
5629             if (n < 6)
5630                 return FALSE;
5631         }
5632     }
5633     return TRUE;
5634 }
5635
5636 static
5637 CHAR_CHECK_CB(pw_char_check)
5638 {
5639     return (isgraph(ch) ? TRUE : FALSE);
5640 }
5641
5642 static void
5643 demo_forms(void)
5644 {
5645     WINDOW *w;
5646     FORM *form;
5647     FIELD *f[12], *secure;
5648     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
5649     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
5650     int finished = 0, c;
5651     unsigned n = 0;
5652
5653 #ifdef NCURSES_MOUSE_VERSION
5654     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5655 #endif
5656
5657     move(18, 0);
5658     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
5659     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
5660     addstr("Home -- go to first field      End -- go to last field\n");
5661     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
5662     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
5663     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
5664     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
5665     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
5666     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
5667     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
5668     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
5669
5670     mvaddstr(4, 57, "Forms Entry Test");
5671
5672     refresh();
5673
5674     /* describe the form */
5675     memset(f, 0, sizeof(f));
5676     f[n++] = make_label(0, 15, "Sample Form");
5677
5678     f[n++] = make_label(2, 0, "Last Name");
5679     f[n++] = make_field(3, 0, 1, 18, FALSE);
5680     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5681
5682     f[n++] = make_label(2, 20, "First Name");
5683     f[n++] = make_field(3, 20, 1, 12, FALSE);
5684     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5685
5686     f[n++] = make_label(2, 34, "Middle Name");
5687     f[n++] = make_field(3, 34, 1, 12, FALSE);
5688     set_field_type(f[n - 1], fty_middle);
5689
5690     f[n++] = make_label(5, 0, "Comments");
5691     f[n++] = make_field(6, 0, 4, 46, FALSE);
5692
5693     f[n++] = make_label(5, 20, "Password:");
5694     secure =
5695         f[n++] = make_field(5, 30, 1, 9, TRUE);
5696     set_field_type(f[n - 1], fty_passwd);
5697     f[n++] = (FIELD *) 0;
5698
5699     if ((form = new_form(f)) != 0) {
5700
5701         display_form(form);
5702
5703         w = form_win(form);
5704         raw();
5705         nonl();                 /* lets us read ^M's */
5706         while (!finished) {
5707             switch (form_driver(form, c = form_virtualize(form, w))) {
5708             case E_OK:
5709                 mvaddstr(5, 57, field_buffer(secure, 1));
5710                 clrtoeol();
5711                 refresh();
5712                 break;
5713             case E_UNKNOWN_COMMAND:
5714                 finished = my_form_driver(form, c);
5715                 break;
5716             default:
5717                 beep();
5718                 break;
5719             }
5720         }
5721
5722         erase_form(form);
5723
5724         free_form(form);
5725     }
5726     for (c = 0; f[c] != 0; c++)
5727         free_field(f[c]);
5728     free_fieldtype(fty_middle);
5729     free_fieldtype(fty_passwd);
5730     noraw();
5731     nl();
5732
5733 #ifdef NCURSES_MOUSE_VERSION
5734     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5735 #endif
5736 }
5737 #endif /* USE_LIBFORM */
5738
5739 /****************************************************************************
5740  *
5741  * Overlap test
5742  *
5743  ****************************************************************************/
5744
5745 static void
5746 fillwin(WINDOW *win, char ch)
5747 {
5748     int y, x;
5749     int y1, x1;
5750
5751     getmaxyx(win, y1, x1);
5752     for (y = 0; y < y1; y++) {
5753         wmove(win, y, 0);
5754         for (x = 0; x < x1; x++)
5755             waddch(win, UChar(ch));
5756     }
5757 }
5758
5759 static void
5760 crosswin(WINDOW *win, char ch)
5761 {
5762     int y, x;
5763     int y1, x1;
5764
5765     getmaxyx(win, y1, x1);
5766     for (y = 0; y < y1; y++) {
5767         for (x = 0; x < x1; x++)
5768             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
5769                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
5770                 wmove(win, y, x);
5771                 waddch(win, UChar(ch));
5772             }
5773     }
5774 }
5775
5776 #define OVERLAP_FLAVORS 5
5777
5778 static void
5779 overlap_helpitem(int state, int item, char *message)
5780 {
5781     int row = (item / 2);
5782     int col = ((item % 2) ? COLS / 2 : 0);
5783
5784     move(LINES - 6 + row, col);
5785     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
5786     clrtoeol();
5787 }
5788
5789 static void
5790 overlap_test_1_attr(WINDOW *win, int flavor, int col)
5791 {
5792     short cpair = (short) (1 + (flavor * 2) + col);
5793
5794     switch (flavor) {
5795     case 0:
5796         wattrset(win, A_NORMAL);
5797         break;
5798     case 1:
5799         wattrset(win, A_BOLD);
5800         break;
5801     case 2:
5802         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
5803         wattrset(win, COLOR_PAIR(cpair) | A_NORMAL);
5804         break;
5805     case 3:
5806         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
5807         wattrset(win, COLOR_PAIR(cpair) | A_BOLD);
5808         break;
5809     }
5810 }
5811
5812 static void
5813 overlap_test_2_attr(WINDOW *win, int flavor, int col)
5814 {
5815     short cpair = (short) (9 + (flavor * 2) + col);
5816
5817     switch (flavor) {
5818     case 0:
5819         /* no effect */
5820         break;
5821     case 1:
5822         /* no effect */
5823         break;
5824     case 2:
5825         init_pair(cpair, COLOR_RED, COLOR_GREEN);
5826         wbkgdset(win, ' ' | A_BLINK | COLOR_PAIR(cpair));
5827         break;
5828     case 3:
5829         wbkgdset(win, ' ' | A_NORMAL);
5830         break;
5831     }
5832 }
5833
5834 static int
5835 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
5836 {
5837     int row;
5838     int col;
5839     int item;
5840     const char *ths, *tht;
5841     char msg[80];
5842
5843     if (state < 0)
5844         state += OVERLAP_FLAVORS;
5845     state = state % OVERLAP_FLAVORS;
5846     assert(state >= 0 && state < OVERLAP_FLAVORS);
5847
5848     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
5849         row = item / 2;
5850         col = item % 2;
5851         ths = col ? "B" : "A";
5852         tht = col ? "A" : "B";
5853
5854         switch (row) {
5855         case 0:
5856             flavors[row] = 0;
5857             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
5858             break;
5859         case 1:
5860             if (use_colors) {
5861                 flavors[row] %= 4;
5862             } else {
5863                 flavors[row] %= 2;
5864             }
5865             overlap_test_1_attr(stdscr, flavors[row], col);
5866             sprintf(msg, "fill window %s with letter %s.", ths, ths);
5867             break;
5868         case 2:
5869             if (use_colors) {
5870                 flavors[row] %= 4;
5871             } else {
5872                 flavors[row] %= 2;
5873             }
5874             switch (flavors[row]) {
5875             case 0:
5876                 sprintf(msg, "cross pattern in window %s.", ths);
5877                 break;
5878             case 1:
5879                 sprintf(msg, "draw box in window %s.", ths);
5880                 break;
5881             case 2:
5882                 sprintf(msg, "set background of window %s.", ths);
5883                 break;
5884             case 3:
5885                 sprintf(msg, "reset background of window %s.", ths);
5886                 break;
5887             }
5888             break;
5889         case 3:
5890             flavors[row] = 0;
5891             sprintf(msg, "clear window %s.", ths);
5892             break;
5893         case 4:
5894             flavors[row] %= 4;
5895             switch (flavors[row]) {
5896             case 0:
5897                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
5898                 break;
5899             case 1:
5900                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
5901                 break;
5902             case 2:
5903                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
5904                 break;
5905             case 3:
5906                 sprintf(msg, "overlay %s onto %s.", ths, tht);
5907                 break;
5908             }
5909             break;
5910         }
5911         overlap_helpitem(state, item, msg);
5912         wattrset(stdscr, A_NORMAL);
5913         wbkgdset(stdscr, ' ' | A_NORMAL);
5914     }
5915     move(LINES - 1, 0);
5916     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
5917            state, flavors[state]);
5918
5919     return state;
5920 }
5921
5922 static void
5923 overlap_test_0(WINDOW *a, WINDOW *b)
5924 {
5925     touchwin(a);
5926     touchwin(b);
5927     wnoutrefresh(a);
5928     wnoutrefresh(b);
5929     doupdate();
5930 }
5931
5932 static void
5933 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
5934 {
5935     overlap_test_1_attr(a, flavor, col);
5936     fillwin(a, fill);
5937     wattrset(a, A_NORMAL);
5938 }
5939
5940 static void
5941 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
5942 {
5943     overlap_test_2_attr(a, flavor, col);
5944     switch (flavor) {
5945     case 0:
5946         crosswin(a, fill);
5947         break;
5948     case 1:
5949         box(a, 0, 0);
5950         break;
5951     case 2:
5952         /* done in overlap_test_2_attr */
5953         break;
5954     case 3:
5955         /* done in overlap_test_2_attr */
5956         break;
5957     }
5958 }
5959
5960 static void
5961 overlap_test_3(WINDOW *a)
5962 {
5963     wclear(a);
5964     wmove(a, 0, 0);
5965 }
5966
5967 static void
5968 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
5969 {
5970     switch (flavor) {
5971     case 0:
5972         overwrite(a, b);
5973         break;
5974     case 1:
5975         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
5976         break;
5977     case 2:
5978         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
5979         break;
5980     case 3:
5981         overlay(a, b);
5982         break;
5983     }
5984 }
5985
5986 /* test effects of overlapping windows */
5987 static void
5988 overlap_test(void)
5989 {
5990     int ch;
5991     int state, flavor[OVERLAP_FLAVORS];
5992
5993     WINDOW *win1 = newwin(9, 20, 3, 3);
5994     WINDOW *win2 = newwin(9, 20, 9, 16);
5995
5996     curs_set(0);
5997     raw();
5998     refresh();
5999     move(0, 0);
6000     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
6001     printw("the shared region of two overlapping windows A and B.  The cross\n");
6002     printw("pattern in each window does not overlap the other.\n");
6003
6004     memset(flavor, 0, sizeof(flavor));
6005     state = overlap_help(0, flavor);
6006
6007     while (!isQuit(ch = Getchar()))
6008         switch (ch) {
6009         case 'a':               /* refresh window A first, then B */
6010             overlap_test_0(win1, win2);
6011             break;
6012
6013         case 'b':               /* refresh window B first, then A */
6014             overlap_test_0(win2, win1);
6015             break;
6016
6017         case 'c':               /* fill window A so it's visible */
6018             overlap_test_1(flavor[1], 0, win1, 'A');
6019             break;
6020
6021         case 'd':               /* fill window B so it's visible */
6022             overlap_test_1(flavor[1], 1, win2, 'B');
6023             break;
6024
6025         case 'e':               /* cross test pattern in window A */
6026             overlap_test_2(flavor[2], 0, win1, 'A');
6027             break;
6028
6029         case 'f':               /* cross test pattern in window A */
6030             overlap_test_2(flavor[2], 1, win2, 'B');
6031             break;
6032
6033         case 'g':               /* clear window A */
6034             overlap_test_3(win1);
6035             break;
6036
6037         case 'h':               /* clear window B */
6038             overlap_test_3(win2);
6039             break;
6040
6041         case 'i':               /* overwrite A onto B */
6042             overlap_test_4(flavor[4], win1, win2);
6043             break;
6044
6045         case 'j':               /* overwrite B onto A */
6046             overlap_test_4(flavor[4], win2, win1);
6047             break;
6048
6049         case CTRL('n'):
6050         case KEY_DOWN:
6051             state = overlap_help(state + 1, flavor);
6052             break;
6053
6054         case CTRL('p'):
6055         case KEY_UP:
6056             state = overlap_help(state - 1, flavor);
6057             break;
6058
6059         case ' ':
6060             flavor[state] += 1;
6061             state = overlap_help(state, flavor);
6062             break;
6063
6064         case '?':
6065             state = overlap_help(state, flavor);
6066             break;
6067
6068         default:
6069             beep();
6070             break;
6071         }
6072
6073     delwin(win2);
6074     delwin(win1);
6075     erase();
6076     curs_set(1);
6077     endwin();
6078 }
6079
6080 /****************************************************************************
6081  *
6082  * Main sequence
6083  *
6084  ****************************************************************************/
6085
6086 static bool
6087 do_single_test(const char c)
6088 /* perform a single specified test */
6089 {
6090     switch (c) {
6091     case 'a':
6092         getch_test();
6093         break;
6094
6095 #if USE_WIDEC_SUPPORT
6096     case 'A':
6097         get_wch_test();
6098         break;
6099 #endif
6100
6101     case 'b':
6102         attr_test();
6103         break;
6104
6105 #if USE_WIDEC_SUPPORT
6106     case 'B':
6107         wide_attr_test();
6108         break;
6109 #endif
6110
6111     case 'c':
6112         if (!use_colors)
6113             Cannot("does not support color.");
6114         else
6115             color_test();
6116         break;
6117
6118 #if USE_WIDEC_SUPPORT
6119     case 'C':
6120         if (!use_colors)
6121             Cannot("does not support color.");
6122         else
6123             wide_color_test();
6124         break;
6125 #endif
6126
6127     case 'd':
6128         if (!use_colors)
6129             Cannot("does not support color.");
6130         else if (!can_change_color())
6131             Cannot("has hardwired color values.");
6132         else
6133             color_edit();
6134         break;
6135
6136 #if USE_SOFTKEYS
6137     case 'e':
6138         slk_test();
6139         break;
6140 #endif
6141
6142 #if USE_WIDEC_SUPPORT
6143     case 'E':
6144         wide_slk_test();
6145         break;
6146 #endif
6147     case 'f':
6148         acs_display();
6149         break;
6150
6151 #if USE_WIDEC_SUPPORT
6152     case 'F':
6153         wide_acs_display();
6154         break;
6155 #endif
6156
6157 #if USE_LIBPANEL
6158     case 'o':
6159         demo_panels(init_panel, fill_panel);
6160         break;
6161 #endif
6162
6163 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
6164     case 'O':
6165         demo_panels(init_wide_panel, fill_wide_panel);
6166         break;
6167 #endif
6168
6169     case 'g':
6170         acs_and_scroll();
6171         break;
6172
6173     case 'i':
6174         flushinp_test(stdscr);
6175         break;
6176
6177     case 'k':
6178         test_sgr_attributes();
6179         break;
6180
6181 #if USE_LIBMENU
6182     case 'm':
6183         menu_test();
6184         break;
6185 #endif
6186
6187     case 'p':
6188         demo_pad();
6189         break;
6190
6191 #if USE_LIBFORM
6192     case 'r':
6193         demo_forms();
6194         break;
6195 #endif
6196
6197     case 's':
6198         overlap_test();
6199         break;
6200
6201 #if USE_LIBMENU && defined(TRACE)
6202     case 't':
6203         trace_set();
6204         break;
6205 #endif
6206
6207     case '?':
6208         break;
6209
6210     default:
6211         return FALSE;
6212     }
6213
6214     return TRUE;
6215 }
6216
6217 static void
6218 usage(void)
6219 {
6220     static const char *const tbl[] =
6221     {
6222         "Usage: ncurses [options]"
6223         ,""
6224         ,"Options:"
6225 #ifdef NCURSES_VERSION
6226         ,"  -a f,b   set default-colors (assumed white-on-black)"
6227         ,"  -d       use default-colors if terminal supports them"
6228 #endif
6229 #if USE_SOFTKEYS
6230         ,"  -e fmt   specify format for soft-keys test (e)"
6231 #endif
6232 #if HAVE_RIPOFFLINE
6233         ,"  -f       rip-off footer line (can repeat)"
6234         ,"  -h       rip-off header line (can repeat)"
6235 #endif
6236         ,"  -m       do not use colors"
6237         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6238 #if USE_LIBPANEL
6239         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6240 #endif
6241 #ifdef TRACE
6242         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6243 #endif
6244     };
6245     size_t n;
6246     for (n = 0; n < SIZEOF(tbl); n++)
6247         fprintf(stderr, "%s\n", tbl[n]);
6248     ExitProgram(EXIT_FAILURE);
6249 }
6250
6251 static void
6252 set_terminal_modes(void)
6253 {
6254     noraw();
6255     cbreak();
6256     noecho();
6257     scrollok(stdscr, TRUE);
6258     idlok(stdscr, TRUE);
6259     keypad(stdscr, TRUE);
6260 }
6261
6262 #ifdef SIGUSR1
6263 static RETSIGTYPE
6264 announce_sig(int sig)
6265 {
6266     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6267 }
6268 #endif
6269
6270 #if HAVE_RIPOFFLINE
6271 static int
6272 rip_footer(WINDOW *win, int cols)
6273 {
6274     wbkgd(win, A_REVERSE);
6275     werase(win);
6276     wmove(win, 0, 0);
6277     wprintw(win, "footer: window %p, %d columns", win, cols);
6278     wnoutrefresh(win);
6279     return OK;
6280 }
6281
6282 static int
6283 rip_header(WINDOW *win, int cols)
6284 {
6285     wbkgd(win, A_REVERSE);
6286     werase(win);
6287     wmove(win, 0, 0);
6288     wprintw(win, "header: window %p, %d columns", win, cols);
6289     wnoutrefresh(win);
6290     return OK;
6291 }
6292 #endif /* HAVE_RIPOFFLINE */
6293
6294 static void
6295 main_menu(bool top)
6296 {
6297     char command;
6298
6299     do {
6300         (void) puts("This is the ncurses main menu");
6301         (void) puts("a = keyboard and mouse input test");
6302 #if USE_WIDEC_SUPPORT
6303         (void) puts("A = wide-character keyboard and mouse input test");
6304 #endif
6305         (void) puts("b = character attribute test");
6306 #if USE_WIDEC_SUPPORT
6307         (void) puts("B = wide-character attribute test");
6308 #endif
6309         (void) puts("c = color test pattern");
6310 #if USE_WIDEC_SUPPORT
6311         (void) puts("C = color test pattern using wide-character calls");
6312 #endif
6313         if (top)
6314             (void) puts("d = edit RGB color values");
6315 #if USE_SOFTKEYS
6316         (void) puts("e = exercise soft keys");
6317 #if USE_WIDEC_SUPPORT
6318         (void) puts("E = exercise soft keys using wide-characters");
6319 #endif
6320 #endif
6321         (void) puts("f = display ACS characters");
6322 #if USE_WIDEC_SUPPORT
6323         (void) puts("F = display Wide-ACS characters");
6324 #endif
6325         (void) puts("g = display windows and scrolling");
6326         (void) puts("i = test of flushinp()");
6327         (void) puts("k = display character attributes");
6328 #if USE_LIBMENU
6329         (void) puts("m = menu code test");
6330 #endif
6331 #if USE_LIBPANEL
6332         (void) puts("o = exercise panels library");
6333 #if USE_WIDEC_SUPPORT
6334         (void) puts("O = exercise panels with wide-characters");
6335 #endif
6336 #endif
6337         (void) puts("p = exercise pad features");
6338         (void) puts("q = quit");
6339 #if USE_LIBFORM
6340         (void) puts("r = exercise forms code");
6341 #endif
6342         (void) puts("s = overlapping-refresh test");
6343 #if USE_LIBMENU && defined(TRACE)
6344         (void) puts("t = set trace level");
6345 #endif
6346         (void) puts("? = repeat this command summary");
6347
6348         (void) fputs("> ", stdout);
6349         (void) fflush(stdout);  /* necessary under SVr4 curses */
6350
6351         /*
6352          * This used to be an 'fgets()' call.  However (on Linux, at least)
6353          * mixing stream I/O and 'read()' (used in the library) causes the
6354          * input stream to be flushed when switching between the two.
6355          */
6356         command = 0;
6357         for (;;) {
6358             char ch = '\0';
6359             if (read(fileno(stdin), &ch, 1) <= 0) {
6360                 if (command == 0)
6361                     command = 'q';
6362                 break;
6363             } else if (command == 0 && !isspace(UChar(ch))) {
6364                 command = ch;
6365             } else if (ch == '\n' || ch == '\r') {
6366                 if ((command == 'd') && !top) {
6367                     (void) fputs("Do not nest test-d\n", stdout);
6368                     command = 0;
6369                 }
6370                 if (command != 0)
6371                     break;
6372                 (void) fputs("> ", stdout);
6373                 (void) fflush(stdout);
6374             }
6375         }
6376
6377         if (do_single_test(command)) {
6378             /*
6379              * This may be overkill; it's intended to reset everything back
6380              * to the initial terminal modes so that tests don't get in
6381              * each other's way.
6382              */
6383             flushinp();
6384             set_terminal_modes();
6385             reset_prog_mode();
6386             clear();
6387             refresh();
6388             endwin();
6389             if (command == '?') {
6390                 (void) puts("This is the ncurses capability tester.");
6391                 (void)
6392                     puts("You may select a test from the main menu by typing the");
6393                 (void)
6394                     puts("key letter of the choice (the letter to left of the =)");
6395                 (void)
6396                     puts("at the > prompt.  Type `q' to exit.");
6397             }
6398             continue;
6399         }
6400     } while
6401         (command != 'q');
6402 }
6403
6404 /*+-------------------------------------------------------------------------
6405         main(argc,argv)
6406 --------------------------------------------------------------------------*/
6407
6408 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6409 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6410
6411 int
6412 main(int argc, char *argv[])
6413 {
6414     int c;
6415     int my_e_param = 1;
6416 #ifdef NCURSES_VERSION
6417     int default_fg = COLOR_WHITE;
6418     int default_bg = COLOR_BLACK;
6419     bool assumed_colors = FALSE;
6420     bool default_colors = FALSE;
6421 #endif
6422     char *palette_file = 0;
6423     bool monochrome = FALSE;
6424
6425     setlocale(LC_ALL, "");
6426
6427     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
6428         switch (c) {
6429 #ifdef NCURSES_VERSION
6430         case 'a':
6431             assumed_colors = TRUE;
6432             sscanf(optarg, "%d,%d", &default_fg, &default_bg);
6433             break;
6434         case 'd':
6435             default_colors = TRUE;
6436             break;
6437 #endif
6438         case 'e':
6439             my_e_param = atoi(optarg);
6440 #ifdef NCURSES_VERSION
6441             if (my_e_param > 3) /* allow extended layouts */
6442                 usage();
6443 #else
6444             if (my_e_param > 1)
6445                 usage();
6446 #endif
6447             break;
6448 #if HAVE_RIPOFFLINE
6449         case 'f':
6450             ripoffline(-1, rip_footer);
6451             break;
6452         case 'h':
6453             ripoffline(1, rip_header);
6454             break;
6455 #endif /* HAVE_RIPOFFLINE */
6456         case 'm':
6457             monochrome = TRUE;
6458             break;
6459         case 'p':
6460             palette_file = optarg;
6461             break;
6462 #if USE_LIBPANEL
6463         case 's':
6464             nap_msec = atol(optarg);
6465             break;
6466 #endif
6467 #ifdef TRACE
6468         case 't':
6469             save_trace = (unsigned) strtol(optarg, 0, 0);
6470             break;
6471 #endif
6472         default:
6473             usage();
6474         }
6475     }
6476
6477     /*
6478      * If there's no menus (unlikely for ncurses!), then we'll have to set
6479      * tracing on initially, just in case the user wants to test something that
6480      * doesn't involve wGetchar.
6481      */
6482 #ifdef TRACE
6483     /* enable debugging */
6484 #if !USE_LIBMENU
6485     trace(save_trace);
6486 #else
6487     if (!isatty(fileno(stdin)))
6488         trace(save_trace);
6489 #endif /* USE_LIBMENU */
6490 #endif /* TRACE */
6491
6492 #if USE_SOFTKEYS
6493     /* tell it we're going to play with soft keys */
6494     slk_init(my_e_param);
6495 #endif
6496
6497 #ifdef SIGUSR1
6498     /* set up null signal catcher so we can see what interrupts to getch do */
6499     signal(SIGUSR1, announce_sig);
6500 #endif
6501
6502     /* we must initialize the curses data structure only once */
6503     initscr();
6504     bkgdset(BLANK);
6505
6506     /* tests, in general, will want these modes */
6507     use_colors = monochrome ? FALSE : has_colors();
6508
6509     if (use_colors) {
6510         start_color();
6511 #ifdef NCURSES_VERSION_PATCH
6512         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6513 #if HAVE_USE_DEFAULT_COLORS
6514         if (default_colors) {
6515             use_default_colors();
6516             min_colors = -1;
6517         }
6518 #if NCURSES_VERSION_PATCH >= 20000708
6519         else if (assumed_colors)
6520             assume_default_colors(default_fg, default_bg);
6521 #endif
6522 #endif
6523 #else /* normal SVr4 curses */
6524         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6525 #endif
6526         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6527
6528         if (can_change_color()) {
6529             short cp;
6530             all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
6531             for (cp = 0; cp < max_colors; ++cp) {
6532                 color_content(cp,
6533                               &all_colors[cp].red,
6534                               &all_colors[cp].green,
6535                               &all_colors[cp].blue);
6536             }
6537             if (palette_file != 0) {
6538                 FILE *fp = fopen(palette_file, "r");
6539                 if (fp != 0) {
6540                     char buffer[BUFSIZ];
6541                     int red, green, blue;
6542                     int scale = 1000;
6543                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6544                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6545                             scale = c;
6546                         } else if (sscanf(buffer, "%d:%d %d %d",
6547                                           &c,
6548                                           &red,
6549                                           &green,
6550                                           &blue) == 4
6551                                    && okCOLOR(c)
6552                                    && okRGB(red)
6553                                    && okRGB(green)
6554                                    && okRGB(blue)) {
6555                             all_colors[c].red = (short) ((red * 1000) / scale);
6556                             all_colors[c].green = (short) ((green * 1000) / scale);
6557                             all_colors[c].blue = (short) ((blue * 1000) / scale);
6558                         }
6559                     }
6560                     fclose(fp);
6561                 }
6562             }
6563         }
6564     }
6565     set_terminal_modes();
6566     def_prog_mode();
6567
6568     /*
6569      * Return to terminal mode, so we're guaranteed of being able to
6570      * select terminal commands even if the capabilities are wrong.
6571      */
6572     endwin();
6573
6574 #if HAVE_CURSES_VERSION
6575     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6576 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6577     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6578                   NCURSES_VERSION_MAJOR,
6579                   NCURSES_VERSION_MINOR,
6580                   NCURSES_VERSION_PATCH);
6581 #else
6582     (void) puts("Welcome to ncurses.  Press ? for help.");
6583 #endif
6584
6585     main_menu(TRUE);
6586
6587     ExitProgram(EXIT_SUCCESS);
6588 }
6589
6590 /* ncurses.c ends here */