]> ncurses.scripts.mit.edu Git - ncurses.git/blob - test/ncurses.c
c1f700cac8e59d4aef25483a5a72941ea6301bc7
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2008,2009 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /****************************************************************************
29
30 NAME
31    ncurses.c --- ncurses library exerciser
32
33 SYNOPSIS
34    ncurses
35
36 DESCRIPTION
37    An interactive test module for the ncurses library.
38
39 AUTHOR
40    Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
41            Thomas E. Dickey (beginning revision 1.27 in 1996).
42
43 $Id: ncurses.c,v 1.338 2009/03/28 21:40:51 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 if (color < 0) {
1863             strcpy(temp, "default");
1864         } else {
1865             strcpy(temp, the_color_names[color]);
1866         }
1867         printw("%-*.*s", width, width, temp);
1868     }
1869 }
1870
1871 static void
1872 color_legend(WINDOW *helpwin, bool wide)
1873 {
1874     int row = 1;
1875     int col = 1;
1876
1877     mvwprintw(helpwin, row++, col,
1878               "ESC to exit.");
1879     ++row;
1880     mvwprintw(helpwin, row++, col,
1881               "Use up/down arrow to scroll through the display if it is");
1882     mvwprintw(helpwin, row++, col,
1883               "longer than one screen. Control/N and Control/P can be used");
1884     mvwprintw(helpwin, row++, col,
1885               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
1886     mvwprintw(helpwin, row++, col,
1887               "full screen; control/B and control/F can be used here.");
1888     ++row;
1889     mvwprintw(helpwin, row++, col,
1890               "Toggles:");
1891     mvwprintw(helpwin, row++, col,
1892               "  a/A     toggle altcharset off/on");
1893     mvwprintw(helpwin, row++, col,
1894               "  b/B     toggle bold off/on");
1895     mvwprintw(helpwin, row++, col,
1896               "  n/N     toggle text/number on/off");
1897     mvwprintw(helpwin, row++, col,
1898               "  r/R     toggle reverse on/off");
1899     mvwprintw(helpwin, row++, col,
1900               "  w/W     toggle width between 8/16 colors");
1901 #if USE_WIDEC_SUPPORT
1902     if (wide) {
1903         mvwprintw(helpwin, row++, col,
1904                   "Wide characters:");
1905         mvwprintw(helpwin, row++, col,
1906                   "  x/X     toggle text between ASCII and wide-character");
1907     }
1908 #else
1909     (void) wide;
1910 #endif
1911 }
1912
1913 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
1914
1915 /* generate a color test pattern */
1916 static void
1917 color_test(void)
1918 {
1919     short i;
1920     int top = 0, width;
1921     int base_row = 0;
1922     int grid_top = top + 3;
1923     int page_size = (LINES - grid_top);
1924     int pairs_max = PAIR_NUMBER(A_COLOR) + 1;
1925     int row_limit;
1926     int per_row;
1927     char numbered[80];
1928     const char *hello;
1929     bool done = FALSE;
1930     bool opt_acsc = FALSE;
1931     bool opt_bold = FALSE;
1932     bool opt_revs = FALSE;
1933     bool opt_nums = FALSE;
1934     bool opt_wide = FALSE;
1935     WINDOW *helpwin;
1936
1937     if (COLORS * COLORS == COLOR_PAIRS) {
1938         int limit = (COLORS - min_colors) * (COLORS - min_colors);
1939         if (pairs_max > limit)
1940             pairs_max = limit;
1941     } else {
1942         if (pairs_max > COLOR_PAIRS)
1943             pairs_max = COLOR_PAIRS;
1944     }
1945
1946     while (!done) {
1947         int shown = 0;
1948
1949         /* this assumes an 80-column line */
1950         if (opt_wide) {
1951             width = 4;
1952             hello = "Test";
1953             per_row = (COLORS > 8) ? 16 : 8;
1954         } else {
1955             width = 8;
1956             hello = "Hello";
1957             per_row = 8;
1958         }
1959         per_row -= min_colors;
1960
1961         row_limit = (pairs_max + per_row - 1) / per_row;
1962
1963         move(0, 0);
1964         (void) printw("There are %d color pairs and %d colors%s\n",
1965                       pairs_max, COLORS,
1966                       min_colors ? " besides 'default'" : "");
1967
1968         clrtobot();
1969         (void) mvprintw(top + 1, 0,
1970                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
1971                         row_limit,
1972                         per_row,
1973                         opt_bold ? "on" : "off");
1974
1975         /* show color names/numbers across the top */
1976         for (i = 0; i < per_row; i++)
1977             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
1978
1979         /* show a grid of colors, with color names/ numbers on the left */
1980         for (i = (short) (base_row * per_row); i < pairs_max; i++) {
1981             int row = grid_top + (i / per_row) - base_row;
1982             int col = (i % per_row + 1) * width;
1983             short pair = i;
1984
1985 #define InxToFG(i) (short) ((i % (COLORS - min_colors)) + min_colors)
1986 #define InxToBG(i) (short) ((i / (COLORS - min_colors)) + min_colors)
1987             if (row >= 0 && move(row, col) != ERR) {
1988                 short fg = InxToFG(i);
1989                 short bg = InxToBG(i);
1990
1991                 init_pair(pair, fg, bg);
1992                 attron((attr_t) COLOR_PAIR(pair));
1993                 if (opt_acsc)
1994                     attron((attr_t) A_ALTCHARSET);
1995                 if (opt_bold)
1996                     attron((attr_t) A_BOLD);
1997                 if (opt_revs)
1998                     attron((attr_t) A_REVERSE);
1999
2000                 if (opt_nums) {
2001                     sprintf(numbered, "{%02X}", i);
2002                     hello = numbered;
2003                 }
2004                 printw("%-*.*s", width, width, hello);
2005                 attrset(A_NORMAL);
2006
2007                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2008                     show_color_name(row, 0, InxToBG(i), opt_wide);
2009                 }
2010                 ++shown;
2011             } else if (shown) {
2012                 break;
2013             }
2014         }
2015
2016         switch (wGetchar(stdscr)) {
2017         case 'a':
2018             opt_acsc = FALSE;
2019             break;
2020         case 'A':
2021             opt_acsc = TRUE;
2022             break;
2023         case 'b':
2024             opt_bold = FALSE;
2025             break;
2026         case 'B':
2027             opt_bold = TRUE;
2028             break;
2029         case 'n':
2030             opt_nums = FALSE;
2031             break;
2032         case 'N':
2033             opt_nums = TRUE;
2034             break;
2035         case 'r':
2036             opt_revs = FALSE;
2037             break;
2038         case 'R':
2039             opt_revs = TRUE;
2040             break;
2041         case case_QUIT:
2042             done = TRUE;
2043             continue;
2044         case 'w':
2045             set_color_test(opt_wide, FALSE);
2046             break;
2047         case 'W':
2048             set_color_test(opt_wide, TRUE);
2049             break;
2050         case CTRL('p'):
2051         case KEY_UP:
2052             if (base_row <= 0) {
2053                 beep();
2054             } else {
2055                 base_row -= 1;
2056             }
2057             break;
2058         case CTRL('n'):
2059         case KEY_DOWN:
2060             if (base_row + page_size >= row_limit) {
2061                 beep();
2062             } else {
2063                 base_row += 1;
2064             }
2065             break;
2066         case CTRL('b'):
2067         case KEY_PREVIOUS:
2068         case KEY_PPAGE:
2069             if (base_row <= 0) {
2070                 beep();
2071             } else {
2072                 base_row -= (page_size - 1);
2073                 if (base_row < 0)
2074                     base_row = 0;
2075             }
2076             break;
2077         case CTRL('f'):
2078         case KEY_NEXT:
2079         case KEY_NPAGE:
2080             if (base_row + page_size >= row_limit) {
2081                 beep();
2082             } else {
2083                 base_row += page_size - 1;
2084                 if (base_row + page_size >= row_limit) {
2085                     base_row = row_limit - page_size - 1;
2086                 }
2087             }
2088             break;
2089         case '?':
2090             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2091                 box(helpwin, 0, 0);
2092                 color_legend(helpwin, FALSE);
2093                 wGetchar(helpwin);
2094                 delwin(helpwin);
2095             }
2096             break;
2097         default:
2098             beep();
2099             continue;
2100         }
2101     }
2102
2103     erase();
2104     endwin();
2105 }
2106
2107 #if USE_WIDEC_SUPPORT
2108 /* generate a color test pattern */
2109 static void
2110 wide_color_test(void)
2111 {
2112     int c;
2113     int i;
2114     int top = 0, width;
2115     int base_row = 0;
2116     int grid_top = top + 3;
2117     int page_size = (LINES - grid_top);
2118     int pairs_max = (unsigned short) (-1);
2119     int row_limit;
2120     int per_row;
2121     char numbered[80];
2122     const char *hello;
2123     bool done = FALSE;
2124     bool opt_acsc = FALSE;
2125     bool opt_bold = FALSE;
2126     bool opt_revs = FALSE;
2127     bool opt_wide = FALSE;
2128     bool opt_nums = FALSE;
2129     bool opt_xchr = FALSE;
2130     wchar_t buffer[10];
2131     WINDOW *helpwin;
2132
2133     if (COLORS * COLORS == COLOR_PAIRS) {
2134         int limit = (COLORS - min_colors) * (COLORS - min_colors);
2135         if (pairs_max > limit)
2136             pairs_max = limit;
2137     } else {
2138         if (pairs_max > COLOR_PAIRS)
2139             pairs_max = COLOR_PAIRS;
2140     }
2141
2142     while (!done) {
2143         int shown = 0;
2144
2145         /* this assumes an 80-column line */
2146         if (opt_wide) {
2147             width = 4;
2148             hello = "Test";
2149             per_row = (COLORS > 8) ? 16 : 8;
2150         } else {
2151             width = 8;
2152             hello = "Hello";
2153             per_row = 8;
2154         }
2155         per_row -= min_colors;
2156
2157         if (opt_xchr) {
2158             make_fullwidth_text(buffer, hello);
2159             width *= 2;
2160             per_row /= 2;
2161         } else {
2162             make_narrow_text(buffer, hello);
2163         }
2164
2165         row_limit = (pairs_max + per_row - 1) / per_row;
2166
2167         move(0, 0);
2168         (void) printw("There are %d color pairs and %d colors%s\n",
2169                       pairs_max, COLORS,
2170                       min_colors ? " besides 'default'" : "");
2171
2172         clrtobot();
2173         (void) mvprintw(top + 1, 0,
2174                         "%dx%d matrix of foreground/background colors, bold *%s*\n",
2175                         row_limit,
2176                         per_row,
2177                         opt_bold ? "on" : "off");
2178
2179         /* show color names/numbers across the top */
2180         for (i = 0; i < per_row; i++)
2181             show_color_name(top + 2, (i + 1) * width, i + min_colors, opt_wide);
2182
2183         /* show a grid of colors, with color names/ numbers on the left */
2184         for (i = (base_row * per_row); i < pairs_max; i++) {
2185             int row = grid_top + (i / per_row) - base_row;
2186             int col = (i % per_row + 1) * width;
2187             short pair = (short) i;
2188
2189             if (row >= 0 && move(row, col) != ERR) {
2190                 init_pair(pair, InxToFG(i), InxToBG(i));
2191                 color_set(pair, NULL);
2192                 if (opt_acsc)
2193                     attr_on((attr_t) A_ALTCHARSET, NULL);
2194                 if (opt_bold)
2195                     attr_on((attr_t) A_BOLD, NULL);
2196                 if (opt_revs)
2197                     attr_on((attr_t) A_REVERSE, NULL);
2198
2199                 if (opt_nums) {
2200                     sprintf(numbered, "{%02X}", i);
2201                     if (opt_xchr) {
2202                         make_fullwidth_text(buffer, numbered);
2203                     } else {
2204                         make_narrow_text(buffer, numbered);
2205                     }
2206                 }
2207                 addnwstr(buffer, width);
2208                 attr_set(A_NORMAL, 0, NULL);
2209
2210                 if ((i % per_row) == 0 && InxToFG(i) == min_colors) {
2211                     show_color_name(row, 0, InxToBG(i), opt_wide);
2212                 }
2213                 ++shown;
2214             } else if (shown) {
2215                 break;
2216             }
2217         }
2218
2219         switch (c = wGetchar(stdscr)) {
2220         case 'a':
2221             opt_acsc = FALSE;
2222             break;
2223         case 'A':
2224             opt_acsc = TRUE;
2225             break;
2226         case 'b':
2227             opt_bold = FALSE;
2228             break;
2229         case 'B':
2230             opt_bold = TRUE;
2231             break;
2232         case 'n':
2233             opt_nums = FALSE;
2234             break;
2235         case 'N':
2236             opt_nums = TRUE;
2237             break;
2238         case 'r':
2239             opt_revs = FALSE;
2240             break;
2241         case 'R':
2242             opt_revs = TRUE;
2243             break;
2244         case case_QUIT:
2245             done = TRUE;
2246             continue;
2247         case 'w':
2248             set_color_test(opt_wide, FALSE);
2249             break;
2250         case 'W':
2251             set_color_test(opt_wide, TRUE);
2252             break;
2253         case 'x':
2254             opt_xchr = FALSE;
2255             break;
2256         case 'X':
2257             opt_xchr = TRUE;
2258             break;
2259         case CTRL('p'):
2260         case KEY_UP:
2261             if (base_row <= 0) {
2262                 beep();
2263             } else {
2264                 base_row -= 1;
2265             }
2266             break;
2267         case CTRL('n'):
2268         case KEY_DOWN:
2269             if (base_row + page_size >= row_limit) {
2270                 beep();
2271             } else {
2272                 base_row += 1;
2273             }
2274             break;
2275         case CTRL('b'):
2276         case KEY_PREVIOUS:
2277         case KEY_PPAGE:
2278             if (base_row <= 0) {
2279                 beep();
2280             } else {
2281                 base_row -= (page_size - 1);
2282                 if (base_row < 0)
2283                     base_row = 0;
2284             }
2285             break;
2286         case CTRL('f'):
2287         case KEY_NEXT:
2288         case KEY_NPAGE:
2289             if (base_row + page_size >= row_limit) {
2290                 beep();
2291             } else {
2292                 base_row += page_size - 1;
2293                 if (base_row + page_size >= row_limit) {
2294                     base_row = row_limit - page_size - 1;
2295                 }
2296             }
2297             break;
2298         case '?':
2299             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2300                 box(helpwin, 0, 0);
2301                 color_legend(helpwin, TRUE);
2302                 wGetchar(helpwin);
2303                 delwin(helpwin);
2304             }
2305             break;
2306         default:
2307             beep();
2308             continue;
2309         }
2310     }
2311
2312     erase();
2313     endwin();
2314 }
2315 #endif /* USE_WIDEC_SUPPORT */
2316
2317 static void
2318 change_color(short current, int field, int value, int usebase)
2319 {
2320     short red, green, blue;
2321
2322     color_content(current, &red, &green, &blue);
2323
2324     switch (field) {
2325     case 0:
2326         red = (short) (usebase ? (red + value) : value);
2327         break;
2328     case 1:
2329         green = (short) (usebase ? (green + value) : value);
2330         break;
2331     case 2:
2332         blue = (short) (usebase ? (blue + value) : value);
2333         break;
2334     }
2335
2336     if (init_color(current, red, green, blue) == ERR)
2337         beep();
2338 }
2339
2340 static void
2341 init_all_colors(void)
2342 {
2343     short c;
2344
2345     for (c = 0; c < COLORS; ++c)
2346         init_color(c,
2347                    all_colors[c].red,
2348                    all_colors[c].green,
2349                    all_colors[c].blue);
2350 }
2351
2352 #define scaled_rgb(n) ((255 * (n)) / 1000)
2353
2354 static void
2355 color_edit(void)
2356 /* display the color test pattern, without trying to edit colors */
2357 {
2358     int i;
2359     int current = 0;
2360     int this_c = 0, value = 0, field = 0;
2361     int last_c;
2362     int top_color = 0;
2363     int page_size = (LINES - 6);
2364
2365     init_all_colors();
2366     refresh();
2367
2368     for (i = 0; i < max_colors; i++)
2369         init_pair((short) i, (short) COLOR_WHITE, (short) i);
2370
2371     mvprintw(LINES - 2, 0, "Number: %d", value);
2372
2373     do {
2374         short red, green, blue;
2375
2376         attron(A_BOLD);
2377         mvaddstr(0, 20, "Color RGB Value Editing");
2378         attroff(A_BOLD);
2379
2380         for (i = (short) top_color;
2381              (i - top_color < page_size)
2382              && (i < max_colors); i++) {
2383             char numeric[80];
2384
2385             sprintf(numeric, "[%d]", i);
2386             mvprintw(2 + i - top_color, 0, "%c %-8s:",
2387                      (i == current ? '>' : ' '),
2388                      (i < (int) SIZEOF(the_color_names)
2389                       ? the_color_names[i] : numeric));
2390             attrset(COLOR_PAIR(i));
2391             addstr("        ");
2392             attrset(A_NORMAL);
2393
2394             color_content((short) i, &red, &green, &blue);
2395             addstr("   R = ");
2396             if (current == i && field == 0)
2397                 attron(A_STANDOUT);
2398             printw("%04d", red);
2399             if (current == i && field == 0)
2400                 attrset(A_NORMAL);
2401             addstr(", G = ");
2402             if (current == i && field == 1)
2403                 attron(A_STANDOUT);
2404             printw("%04d", green);
2405             if (current == i && field == 1)
2406                 attrset(A_NORMAL);
2407             addstr(", B = ");
2408             if (current == i && field == 2)
2409                 attron(A_STANDOUT);
2410             printw("%04d", blue);
2411             if (current == i && field == 2)
2412                 attrset(A_NORMAL);
2413             attrset(A_NORMAL);
2414             printw(" ( %3d %3d %3d )",
2415                    scaled_rgb(red),
2416                    scaled_rgb(green),
2417                    scaled_rgb(blue));
2418         }
2419
2420         mvaddstr(LINES - 3, 0,
2421                  "Use up/down to select a color, left/right to change fields.");
2422         mvaddstr(LINES - 2, 0,
2423                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2424
2425         move(2 + current - top_color, 0);
2426
2427         last_c = this_c;
2428         this_c = Getchar();
2429         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2430             value = 0;
2431
2432         switch (this_c) {
2433         case CTRL('b'):
2434         case KEY_PPAGE:
2435             if (current > 0)
2436                 current -= (page_size - 1);
2437             else
2438                 beep();
2439             break;
2440
2441         case CTRL('f'):
2442         case KEY_NPAGE:
2443             if (current < (max_colors - 1))
2444                 current += (page_size - 1);
2445             else
2446                 beep();
2447             break;
2448
2449         case CTRL('p'):
2450         case KEY_UP:
2451             current = (current == 0 ? (max_colors - 1) : current - 1);
2452             break;
2453
2454         case CTRL('n'):
2455         case KEY_DOWN:
2456             current = (current == (max_colors - 1) ? 0 : current + 1);
2457             break;
2458
2459         case KEY_RIGHT:
2460             field = (field == 2 ? 0 : field + 1);
2461             break;
2462
2463         case KEY_LEFT:
2464             field = (field == 0 ? 2 : field - 1);
2465             break;
2466
2467         case '0':
2468         case '1':
2469         case '2':
2470         case '3':
2471         case '4':
2472         case '5':
2473         case '6':
2474         case '7':
2475         case '8':
2476         case '9':
2477             value = value * 10 + (this_c - '0');
2478             break;
2479
2480         case '+':
2481             change_color((short) current, field, value, 1);
2482             break;
2483
2484         case '-':
2485             change_color((short) current, field, -value, 1);
2486             break;
2487
2488         case '=':
2489             change_color((short) current, field, value, 0);
2490             break;
2491
2492         case '?':
2493             erase();
2494             P("                      RGB Value Editing Help");
2495             P("");
2496             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2497             P("the fields in one of the RGB triples of the current colors; the one");
2498             P("currently selected will be reverse-video highlighted.");
2499             P("");
2500             P("To change a field, enter the digits of the new value; they are echoed");
2501             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2502             P("To increment or decrement a value, use the same procedure, but finish");
2503             P("with a `+' or `-'.");
2504             P("");
2505             P("Press 'm' to invoke the top-level menu with the current color settings.");
2506             P("To quit, do ESC");
2507
2508             Pause();
2509             erase();
2510             break;
2511
2512         case 'm':
2513             endwin();
2514             main_menu(FALSE);
2515             refresh();
2516             break;
2517
2518         case case_QUIT:
2519             break;
2520
2521         default:
2522             beep();
2523             break;
2524         }
2525
2526         if (current < 0)
2527             current = 0;
2528         if (current >= max_colors)
2529             current = max_colors - 1;
2530         if (current < top_color)
2531             top_color = current;
2532         if (current - top_color >= page_size)
2533             top_color = current - (page_size - 1);
2534
2535         mvprintw(LINES - 1, 0, "Number: %d", value);
2536         clrtoeol();
2537     } while
2538         (!isQuit(this_c));
2539
2540     erase();
2541
2542     /*
2543      * ncurses does not reset each color individually when calling endwin().
2544      */
2545     init_all_colors();
2546
2547     endwin();
2548 }
2549
2550 /****************************************************************************
2551  *
2552  * Soft-key label test
2553  *
2554  ****************************************************************************/
2555
2556 #if USE_SOFTKEYS
2557
2558 #define SLK_HELP 17
2559 #define SLK_WORK (SLK_HELP + 3)
2560
2561 static void
2562 slk_help(void)
2563 {
2564     static const char *table[] =
2565     {
2566         "Available commands are:"
2567         ,""
2568         ,"^L         -- repaint this message and activate soft keys"
2569         ,"a/d        -- activate/disable soft keys"
2570         ,"c          -- set centered format for labels"
2571         ,"l          -- set left-justified format for labels"
2572         ,"r          -- set right-justified format for labels"
2573         ,"[12345678] -- set label; labels are numbered 1 through 8"
2574         ,"e          -- erase stdscr (should not erase labels)"
2575         ,"s          -- test scrolling of shortened screen"
2576 #if HAVE_SLK_COLOR
2577         ,"F/B        -- cycle through foreground/background colors"
2578 #endif
2579         ,"ESC        -- return to main menu"
2580         ,""
2581         ,"Note: if activating the soft keys causes your terminal to scroll up"
2582         ,"one line, your terminal auto-scrolls when anything is written to the"
2583         ,"last screen position.  The ncurses code does not yet handle this"
2584         ,"gracefully."
2585     };
2586     unsigned j;
2587
2588     move(2, 0);
2589     for (j = 0; j < SIZEOF(table); ++j) {
2590         P(table[j]);
2591     }
2592     refresh();
2593 }
2594
2595 #if HAVE_SLK_COLOR
2596 static void
2597 call_slk_color(short fg, short bg)
2598 {
2599     init_pair(1, bg, fg);
2600     slk_color(1);
2601     mvprintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
2602     clrtoeol();
2603     refresh();
2604 }
2605 #endif
2606
2607 static void
2608 slk_test(void)
2609 /* exercise the soft keys */
2610 {
2611     int c, fmt = 1;
2612     char buf[9];
2613     char *s;
2614 #if HAVE_SLK_COLOR
2615     short fg = COLOR_BLACK;
2616     short bg = COLOR_WHITE;
2617 #endif
2618
2619     c = CTRL('l');
2620 #if HAVE_SLK_COLOR
2621     if (use_colors) {
2622         call_slk_color(fg, bg);
2623     }
2624 #endif
2625
2626     do {
2627         move(0, 0);
2628         switch (c) {
2629         case CTRL('l'):
2630             erase();
2631             attron(A_BOLD);
2632             mvaddstr(0, 20, "Soft Key Exerciser");
2633             attroff(A_BOLD);
2634
2635             slk_help();
2636             /* fall through */
2637
2638         case 'a':
2639             slk_restore();
2640             break;
2641
2642         case 'e':
2643             wclear(stdscr);
2644             break;
2645
2646         case 's':
2647             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2648             while ((c = Getchar()) != 'Q' && (c != ERR))
2649                 addch((chtype) c);
2650             break;
2651
2652         case 'd':
2653             slk_clear();
2654             break;
2655
2656         case 'l':
2657             fmt = 0;
2658             break;
2659
2660         case 'c':
2661             fmt = 1;
2662             break;
2663
2664         case 'r':
2665             fmt = 2;
2666             break;
2667
2668         case '1':
2669         case '2':
2670         case '3':
2671         case '4':
2672         case '5':
2673         case '6':
2674         case '7':
2675         case '8':
2676             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2677             strcpy(buf, "");
2678             if ((s = slk_label(c - '0')) != 0) {
2679                 strncpy(buf, s, 8);
2680             }
2681             wGetstring(stdscr, buf, 8);
2682             slk_set((c - '0'), buf, fmt);
2683             slk_refresh();
2684             move(SLK_WORK, 0);
2685             clrtobot();
2686             break;
2687
2688         case case_QUIT:
2689             goto done;
2690
2691 #if HAVE_SLK_COLOR
2692         case 'F':
2693             if (use_colors) {
2694                 fg = (short) ((fg + 1) % COLORS);
2695                 call_slk_color(fg, bg);
2696             }
2697             break;
2698         case 'B':
2699             if (use_colors) {
2700                 bg = (short) ((bg + 1) % COLORS);
2701                 call_slk_color(fg, bg);
2702             }
2703             break;
2704 #endif
2705 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2706         case KEY_RESIZE:
2707             wnoutrefresh(stdscr);
2708             break;
2709 #endif
2710
2711         default:
2712             beep();
2713         }
2714     } while (!isQuit(c = Getchar()));
2715
2716   done:
2717     slk_clear();
2718     erase();
2719     endwin();
2720 }
2721
2722 #if USE_WIDEC_SUPPORT
2723 #define SLKLEN 8
2724 static void
2725 wide_slk_test(void)
2726 /* exercise the soft keys */
2727 {
2728     int c, fmt = 1;
2729     wchar_t buf[SLKLEN + 1];
2730     char *s;
2731     short fg = COLOR_BLACK;
2732     short bg = COLOR_WHITE;
2733
2734     c = CTRL('l');
2735     if (use_colors) {
2736         call_slk_color(fg, bg);
2737     }
2738     do {
2739         move(0, 0);
2740         switch (c) {
2741         case CTRL('l'):
2742             erase();
2743             attr_on(WA_BOLD, NULL);
2744             mvaddstr(0, 20, "Soft Key Exerciser");
2745             attr_off(WA_BOLD, NULL);
2746
2747             slk_help();
2748             /* fall through */
2749
2750         case 'a':
2751             slk_restore();
2752             break;
2753
2754         case 'e':
2755             wclear(stdscr);
2756             break;
2757
2758         case 's':
2759             mvprintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
2760             while ((c = Getchar()) != 'Q' && (c != ERR))
2761                 addch((chtype) c);
2762             break;
2763
2764         case 'd':
2765             slk_clear();
2766             break;
2767
2768         case 'l':
2769             fmt = 0;
2770             break;
2771
2772         case 'c':
2773             fmt = 1;
2774             break;
2775
2776         case 'r':
2777             fmt = 2;
2778             break;
2779
2780         case '1':
2781         case '2':
2782         case '3':
2783         case '4':
2784         case '5':
2785         case '6':
2786         case '7':
2787         case '8':
2788             (void) mvaddstr(SLK_WORK, 0, "Please enter the label value: ");
2789             *buf = 0;
2790             if ((s = slk_label(c - '0')) != 0) {
2791                 char *temp = strdup(s);
2792                 size_t used = strlen(temp);
2793                 size_t want = SLKLEN;
2794                 size_t test;
2795 #ifndef state_unused
2796                 mbstate_t state;
2797 #endif
2798
2799                 buf[0] = L'\0';
2800                 while (want > 0 && used != 0) {
2801                     const char *base = s;
2802                     reset_mbytes(state);
2803                     test = count_mbytes(base, 0, &state);
2804                     if (test == (size_t) -1) {
2805                         temp[--used] = 0;
2806                     } else if (test > want) {
2807                         temp[--used] = 0;
2808                     } else {
2809                         reset_mbytes(state);
2810                         trans_mbytes(buf, base, want, &state);
2811                         break;
2812                     }
2813                 }
2814                 free(temp);
2815             }
2816             wGet_wstring(stdscr, buf, SLKLEN);
2817             slk_wset((c - '0'), buf, fmt);
2818             slk_refresh();
2819             move(SLK_WORK, 0);
2820             clrtobot();
2821             break;
2822
2823         case case_QUIT:
2824             goto done;
2825
2826         case 'F':
2827             if (use_colors) {
2828                 fg = (short) ((fg + 1) % COLORS);
2829                 call_slk_color(fg, bg);
2830             }
2831             break;
2832         case 'B':
2833             if (use_colors) {
2834                 bg = (short) ((bg + 1) % COLORS);
2835                 call_slk_color(fg, bg);
2836             }
2837             break;
2838 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
2839         case KEY_RESIZE:
2840             wnoutrefresh(stdscr);
2841             break;
2842 #endif
2843         default:
2844             beep();
2845         }
2846     } while (!isQuit(c = Getchar()));
2847
2848   done:
2849     slk_clear();
2850     erase();
2851     endwin();
2852 }
2853 #endif
2854 #endif /* SLK_INIT */
2855
2856 /****************************************************************************
2857  *
2858  * Alternate character-set stuff
2859  *
2860  ****************************************************************************/
2861 /* *INDENT-OFF* */
2862 static struct {
2863     chtype attr;
2864     const char *name;
2865 } attrs_to_cycle[] = {
2866     { A_NORMAL,         "normal" },
2867     { A_BOLD,           "bold" },
2868     { A_BLINK,          "blink" },
2869     { A_REVERSE,        "reverse" },
2870     { A_UNDERLINE,      "underline" },
2871 };
2872 /* *INDENT-ON* */
2873
2874 static bool
2875 cycle_attr(int ch, unsigned *at_code, chtype *attr)
2876 {
2877     bool result = TRUE;
2878
2879     switch (ch) {
2880     case 'v':
2881         if ((*at_code += 1) >= SIZEOF(attrs_to_cycle))
2882             *at_code = 0;
2883         break;
2884     case 'V':
2885         if (*at_code == 1)
2886             *at_code = SIZEOF(attrs_to_cycle) - 1;
2887         else
2888             *at_code -= 1;
2889         break;
2890     default:
2891         result = FALSE;
2892         break;
2893     }
2894     if (result)
2895         *attr = attrs_to_cycle[*at_code].attr;
2896     return result;
2897 }
2898
2899 static bool
2900 cycle_colors(int ch, int *fg, int *bg, short *pair)
2901 {
2902     bool result = FALSE;
2903
2904     if (use_colors) {
2905         result = TRUE;
2906         switch (ch) {
2907         case 'F':
2908             if ((*fg -= 1) < 0)
2909                 *fg = COLORS - 1;
2910             break;
2911         case 'f':
2912             if ((*fg += 1) >= COLORS)
2913                 *fg = 0;
2914             break;
2915         case 'B':
2916             if ((*bg -= 1) < 0)
2917                 *bg = COLORS - 1;
2918             break;
2919         case 'b':
2920             if ((*bg += 1) >= COLORS)
2921                 *bg = 0;
2922             break;
2923         default:
2924             result = FALSE;
2925             break;
2926         }
2927         if (result) {
2928             *pair = (short) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
2929             if (*pair != 0) {
2930                 *pair = 1;
2931                 if (init_pair(*pair, (short) *fg, (short) *bg) == ERR) {
2932                     result = FALSE;
2933                 }
2934             }
2935         }
2936     }
2937     return result;
2938 }
2939
2940 /* ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
2941  * terminal to perform functions.  The remaining codes can be graphic.
2942  */
2943 static void
2944 show_upper_chars(unsigned first, int repeat, attr_t attr, short pair)
2945 {
2946     bool C1 = (first == 128);
2947     unsigned code;
2948     unsigned last = first + 31;
2949     int reply;
2950
2951     erase();
2952     attron(A_BOLD);
2953     mvprintw(0, 20, "Display of %s Character Codes %d to %d",
2954              C1 ? "C1" : "GR", first, last);
2955     attroff(A_BOLD);
2956     refresh();
2957
2958     for (code = first; code <= last; code++) {
2959         int count = repeat;
2960         int row = 2 + ((int) (code - first) % 16);
2961         int col = ((int) (code - first) / 16) * COLS / 2;
2962         char tmp[80];
2963         sprintf(tmp, "%3u (0x%x)", code, code);
2964         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
2965
2966         do {
2967             if (C1)
2968                 nodelay(stdscr, TRUE);
2969             echochar(colored_chtype(code, attr, pair));
2970             if (C1) {
2971                 /* (yes, this _is_ crude) */
2972                 while ((reply = Getchar()) != ERR) {
2973                     addch(UChar(reply));
2974                     napms(10);
2975                 }
2976                 nodelay(stdscr, FALSE);
2977             }
2978         } while (--count > 0);
2979     }
2980 }
2981
2982 #define PC_COLS 4
2983
2984 static void
2985 show_pc_chars(int repeat, attr_t attr, short pair)
2986 {
2987     unsigned code;
2988
2989     erase();
2990     attron(A_BOLD);
2991     mvprintw(0, 20, "Display of PC Character Codes");
2992     attroff(A_BOLD);
2993     refresh();
2994
2995     for (code = 0; code < 16; ++code) {
2996         mvprintw(2, (int) code * PC_COLS + 8, "%X", code);
2997     }
2998     for (code = 0; code < 256; code++) {
2999         int count = repeat;
3000         int row = 3 + (int) (code / 16) + (code >= 128);
3001         int col = 8 + (int) (code % 16) * PC_COLS;
3002         if ((code % 16) == 0)
3003             mvprintw(row, 0, "0x%02x:", code);
3004         move(row, col);
3005         do {
3006             switch (code) {
3007             case '\n':
3008             case '\r':
3009             case '\b':
3010             case '\f':
3011             case '\033':
3012             case 0x9b:
3013                 /*
3014                  * Skip the ones that do not work.
3015                  */
3016                 break;
3017             default:
3018                 addch(colored_chtype(code, A_ALTCHARSET | attr, pair));
3019                 break;
3020             }
3021         } while (--count > 0);
3022     }
3023 }
3024
3025 static void
3026 show_box_chars(int repeat, attr_t attr, short pair)
3027 {
3028     (void) repeat;
3029     attr |= COLOR_PAIR(pair);
3030
3031     erase();
3032     attron(A_BOLD);
3033     mvaddstr(0, 20, "Display of the ACS Line-Drawing Set");
3034     attroff(A_BOLD);
3035     refresh();
3036     /* *INDENT-OFF* */
3037     wborder(stdscr,
3038             colored_chtype(ACS_VLINE,    attr, pair),
3039             colored_chtype(ACS_VLINE,    attr, pair),
3040             colored_chtype(ACS_HLINE,    attr, pair),
3041             colored_chtype(ACS_HLINE,    attr, pair),
3042             colored_chtype(ACS_ULCORNER, attr, pair),
3043             colored_chtype(ACS_URCORNER, attr, pair),
3044             colored_chtype(ACS_LLCORNER, attr, pair),
3045             colored_chtype(ACS_LRCORNER, attr, pair));
3046     mvhline(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3047     mvvline(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3048     mvaddch(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3049     mvaddch(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3050     mvaddch(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3051     mvaddch(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3052     mvaddch(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3053     /* *INDENT-ON* */
3054
3055 }
3056
3057 static int
3058 show_1_acs(int n, int repeat, const char *name, chtype code)
3059 {
3060     const int height = 16;
3061     int row = 2 + (n % height);
3062     int col = (n / height) * COLS / 2;
3063
3064     mvprintw(row, col, "%*s : ", COLS / 4, name);
3065     do {
3066         addch(code);
3067     } while (--repeat > 0);
3068     return n + 1;
3069 }
3070
3071 static void
3072 show_acs_chars(int repeat, attr_t attr, short pair)
3073 /* display the ACS character set */
3074 {
3075     int n;
3076
3077 #define BOTH(name) #name, colored_chtype(name, attr, pair)
3078
3079     erase();
3080     attron(A_BOLD);
3081     mvaddstr(0, 20, "Display of the ACS Character Set");
3082     attroff(A_BOLD);
3083     refresh();
3084
3085     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3086     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3087     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3088     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3089
3090     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3091     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3092     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3093     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3094
3095     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3096     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3097
3098     /*
3099      * HPUX's ACS definitions are broken here.  Just give up.
3100      */
3101 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3102     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3103     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3104     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3105     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3106
3107     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3108     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3109     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3110     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3111     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3112     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3113     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3114     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3115     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3116
3117     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3118     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3119     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3120
3121     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3122     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3123     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3124     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3125     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3126     n = show_1_acs(n, repeat, BOTH(ACS_S9));
3127 #endif
3128 }
3129
3130 static void
3131 acs_display(void)
3132 {
3133     int c = 'a';
3134     char *term = getenv("TERM");
3135     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3136                               ? "p=PC, "
3137                               : "");
3138     chtype attr = A_NORMAL;
3139     int digit = 0;
3140     int repeat = 1;
3141     int fg = COLOR_BLACK;
3142     int bg = COLOR_BLACK;
3143     unsigned at_code = 0;
3144     short pair = 0;
3145     void (*last_show_acs) (int, attr_t, short) = 0;
3146
3147     do {
3148         switch (c) {
3149         case CTRL('L'):
3150             Repaint();
3151             break;
3152         case 'a':
3153             ToggleAcs(last_show_acs, show_acs_chars);
3154             break;
3155         case 'p':
3156             if (*pch_kludge)
3157                 ToggleAcs(last_show_acs, show_pc_chars);
3158             else
3159                 beep();
3160             break;
3161         case 'x':
3162             ToggleAcs(last_show_acs, show_box_chars);
3163             break;
3164         case '0':
3165         case '1':
3166         case '2':
3167         case '3':
3168             digit = (c - '0');
3169             last_show_acs = 0;
3170             break;
3171         case '-':
3172             if (digit > 0) {
3173                 --digit;
3174                 last_show_acs = 0;
3175             } else {
3176                 beep();
3177             }
3178             break;
3179         case '+':
3180             if (digit < 3) {
3181                 ++digit;
3182                 last_show_acs = 0;
3183             } else {
3184                 beep();
3185             }
3186             break;
3187         case '>':
3188             if (repeat < (COLS / 4))
3189                 ++repeat;
3190             break;
3191         case '<':
3192             if (repeat > 1)
3193                 --repeat;
3194             break;
3195         default:
3196             if (cycle_attr(c, &at_code, &attr)
3197                 || cycle_colors(c, &fg, &bg, &pair)) {
3198                 break;
3199             } else {
3200                 beep();
3201             }
3202             break;
3203         }
3204         if (last_show_acs != 0)
3205             last_show_acs(repeat, attr, pair);
3206         else
3207             show_upper_chars((unsigned) (digit * 32 + 128), repeat, attr, pair);
3208
3209         mvprintw(LINES - 3, 0,
3210                  "Note: ANSI terminals may not display C1 characters.");
3211         mvprintw(LINES - 2, 0,
3212                  "Select: a=ACS, x=box, %s0=C1, 1-3,+/- non-ASCII, </> repeat, ESC=quit",
3213                  pch_kludge);
3214         if (use_colors) {
3215             mvprintw(LINES - 1, 0,
3216                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3217                      attrs_to_cycle[at_code].name,
3218                      fg, bg);
3219         } else {
3220             mvprintw(LINES - 1, 0,
3221                      "v/V cycles through video attributes (%s).",
3222                      attrs_to_cycle[at_code].name);
3223         }
3224         refresh();
3225     } while (!isQuit(c = Getchar()));
3226
3227     Pause();
3228     erase();
3229     endwin();
3230 }
3231
3232 #if USE_WIDEC_SUPPORT
3233 static cchar_t *
3234 merge_wide_attr(cchar_t *dst, const cchar_t *src, attr_t attr, short pair)
3235 {
3236     int count = getcchar(src, NULL, NULL, NULL, 0);
3237     wchar_t *wch = 0;
3238     attr_t ignore_attr;
3239     short ignore_pair;
3240
3241     *dst = *src;
3242     if (count > 0) {
3243         if ((wch = typeMalloc(wchar_t, (unsigned) count + 1)) != 0) {
3244             if (getcchar(src, wch, &ignore_attr, &ignore_pair, 0) != ERR) {
3245                 attr |= (ignore_attr & A_ALTCHARSET);
3246                 setcchar(dst, wch, attr, pair, 0);
3247             }
3248             free(wch);
3249         }
3250     }
3251     return dst;
3252 }
3253
3254 static void
3255 show_upper_widechars(int first, int repeat, int space, attr_t attr, short pair)
3256 {
3257     cchar_t temp;
3258     wchar_t code;
3259     int last = first + 31;
3260
3261     erase();
3262     attron(A_BOLD);
3263     mvprintw(0, 20, "Display of Character Codes %d to %d", first, last);
3264     attroff(A_BOLD);
3265
3266     for (code = first; (int) code <= last; code++) {
3267         int row = 2 + ((code - first) % 16);
3268         int col = ((code - first) / 16) * COLS / 2;
3269         wchar_t codes[10];
3270         char tmp[80];
3271         int count = repeat;
3272         int y, x;
3273
3274         memset(&codes, 0, sizeof(codes));
3275         codes[0] = code;
3276         sprintf(tmp, "%3ld (0x%lx)", (long) code, (long) code);
3277         mvprintw(row, col, "%*s: ", COLS / 4, tmp);
3278         setcchar(&temp, codes, attr, pair, 0);
3279         do {
3280             /*
3281              * Give non-spacing characters something to combine with.  If we
3282              * don't, they'll bunch up in a heap on the space after the ":".
3283              * Mark them with reverse-video to make them simpler to find on
3284              * the display.
3285              */
3286             if (wcwidth(code) == 0)
3287                 addch(space | (A_REVERSE ^ attr) | COLOR_PAIR(pair));
3288             /*
3289              * This could use add_wch(), but is done for comparison with the
3290              * normal 'f' test (and to make a test-case for echo_wchar()).
3291              * The screen will flicker because the erase() at the top of the
3292              * function is met by the builtin refresh() in echo_wchar().
3293              */
3294             echo_wchar(&temp);
3295             /*
3296              * The repeat-count may make text wrap - avoid that.
3297              */
3298             getyx(stdscr, y, x);
3299             if (x >= col + (COLS / 2) - 2)
3300                 break;
3301         } while (--count > 0);
3302     }
3303 }
3304
3305 static int
3306 show_1_wacs(int n, int repeat, const char *name, const cchar_t *code)
3307 {
3308     const int height = 16;
3309     int row = 2 + (n % height);
3310     int col = (n / height) * COLS / 2;
3311
3312     mvprintw(row, col, "%*s : ", COLS / 4, name);
3313     while (--repeat >= 0) {
3314         add_wch(code);
3315     }
3316     return n + 1;
3317 }
3318
3319 #define MERGE_ATTR(wch) merge_wide_attr(&temp, wch, attr, pair)
3320
3321 static void
3322 show_wacs_chars(int repeat, attr_t attr, short pair)
3323 /* display the wide-ACS character set */
3324 {
3325     cchar_t temp;
3326
3327     int n;
3328
3329 /*#define BOTH2(name) #name, &(name) */
3330 #define BOTH2(name) #name, MERGE_ATTR(name)
3331
3332     erase();
3333     attron(A_BOLD);
3334     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3335     attroff(A_BOLD);
3336     refresh();
3337
3338     n = show_1_wacs(0, repeat, BOTH2(WACS_ULCORNER));
3339     n = show_1_wacs(n, repeat, BOTH2(WACS_URCORNER));
3340     n = show_1_wacs(n, repeat, BOTH2(WACS_LLCORNER));
3341     n = show_1_wacs(n, repeat, BOTH2(WACS_LRCORNER));
3342
3343     n = show_1_wacs(n, repeat, BOTH2(WACS_LTEE));
3344     n = show_1_wacs(n, repeat, BOTH2(WACS_RTEE));
3345     n = show_1_wacs(n, repeat, BOTH2(WACS_TTEE));
3346     n = show_1_wacs(n, repeat, BOTH2(WACS_BTEE));
3347
3348     n = show_1_wacs(n, repeat, BOTH2(WACS_HLINE));
3349     n = show_1_wacs(n, repeat, BOTH2(WACS_VLINE));
3350
3351     n = show_1_wacs(n, repeat, BOTH2(WACS_LARROW));
3352     n = show_1_wacs(n, repeat, BOTH2(WACS_RARROW));
3353     n = show_1_wacs(n, repeat, BOTH2(WACS_UARROW));
3354     n = show_1_wacs(n, repeat, BOTH2(WACS_DARROW));
3355
3356     n = show_1_wacs(n, repeat, BOTH2(WACS_BLOCK));
3357     n = show_1_wacs(n, repeat, BOTH2(WACS_BOARD));
3358     n = show_1_wacs(n, repeat, BOTH2(WACS_LANTERN));
3359     n = show_1_wacs(n, repeat, BOTH2(WACS_BULLET));
3360     n = show_1_wacs(n, repeat, BOTH2(WACS_CKBOARD));
3361     n = show_1_wacs(n, repeat, BOTH2(WACS_DEGREE));
3362     n = show_1_wacs(n, repeat, BOTH2(WACS_DIAMOND));
3363     n = show_1_wacs(n, repeat, BOTH2(WACS_PLMINUS));
3364     n = show_1_wacs(n, repeat, BOTH2(WACS_PLUS));
3365
3366 #ifdef CURSES_WACS_ARRAY
3367     n = show_1_wacs(n, repeat, BOTH2(WACS_GEQUAL));
3368     n = show_1_wacs(n, repeat, BOTH2(WACS_NEQUAL));
3369     n = show_1_wacs(n, repeat, BOTH2(WACS_LEQUAL));
3370
3371     n = show_1_wacs(n, repeat, BOTH2(WACS_STERLING));
3372     n = show_1_wacs(n, repeat, BOTH2(WACS_PI));
3373     n = show_1_wacs(n, repeat, BOTH2(WACS_S1));
3374     n = show_1_wacs(n, repeat, BOTH2(WACS_S3));
3375     n = show_1_wacs(n, repeat, BOTH2(WACS_S7));
3376     n = show_1_wacs(n, repeat, BOTH2(WACS_S9));
3377 #endif
3378 }
3379
3380 #undef MERGE_ATTR
3381
3382 #define MERGE_ATTR(n,wch) merge_wide_attr(&temp[n], wch, attr, pair)
3383
3384 static void
3385 show_wbox_chars(int repeat, attr_t attr, short pair)
3386 {
3387     cchar_t temp[8];
3388
3389     (void) repeat;
3390     erase();
3391     attron(A_BOLD);
3392     mvaddstr(0, 20, "Display of the Wide-ACS Line-Drawing Set");
3393     attroff(A_BOLD);
3394     refresh();
3395
3396     wborder_set(stdscr,
3397                 MERGE_ATTR(0, WACS_VLINE),
3398                 MERGE_ATTR(1, WACS_VLINE),
3399                 MERGE_ATTR(2, WACS_HLINE),
3400                 MERGE_ATTR(3, WACS_HLINE),
3401                 MERGE_ATTR(4, WACS_ULCORNER),
3402                 MERGE_ATTR(5, WACS_URCORNER),
3403                 MERGE_ATTR(6, WACS_LLCORNER),
3404                 MERGE_ATTR(7, WACS_LRCORNER));
3405     /* *INDENT-OFF* */
3406     mvhline_set(LINES / 2, 0,        MERGE_ATTR(0, WACS_HLINE), COLS);
3407     mvvline_set(0,         COLS / 2, MERGE_ATTR(0, WACS_VLINE), LINES);
3408     mvadd_wch(0,           COLS / 2, MERGE_ATTR(0, WACS_TTEE));
3409     mvadd_wch(LINES / 2,   COLS / 2, MERGE_ATTR(0, WACS_PLUS));
3410     mvadd_wch(LINES - 1,   COLS / 2, MERGE_ATTR(0, WACS_BTEE));
3411     mvadd_wch(LINES / 2,   0,        MERGE_ATTR(0, WACS_LTEE));
3412     mvadd_wch(LINES / 2,   COLS - 1, MERGE_ATTR(0, WACS_RTEE));
3413     /* *INDENT-ON* */
3414
3415 }
3416
3417 #undef MERGE_ATTR
3418
3419 static int
3420 show_2_wacs(int n, const char *name, const char *code, attr_t attr, short pair)
3421 {
3422     const int height = 16;
3423     int row = 2 + (n % height);
3424     int col = (n / height) * COLS / 2;
3425     char temp[80];
3426
3427     mvprintw(row, col, "%*s : ", COLS / 4, name);
3428     attr_set(attr, pair, 0);
3429     addstr(strcpy(temp, code));
3430     attr_set(A_NORMAL, 0, 0);
3431     return n + 1;
3432 }
3433
3434 #define SHOW_UTF8(n, name, code) show_2_wacs(n, name, code, attr, pair)
3435
3436 static void
3437 show_utf8_chars(int repeat, attr_t attr, short pair)
3438 {
3439     int n;
3440
3441     (void) repeat;
3442     erase();
3443     attron(A_BOLD);
3444     mvaddstr(0, 20, "Display of the Wide-ACS Character Set");
3445     attroff(A_BOLD);
3446     refresh();
3447     /* *INDENT-OFF* */
3448     n = SHOW_UTF8(0, "WACS_ULCORNER",   "\342\224\214");
3449     n = SHOW_UTF8(n, "WACS_URCORNER",   "\342\224\220");
3450     n = SHOW_UTF8(n, "WACS_LLCORNER",   "\342\224\224");
3451     n = SHOW_UTF8(n, "WACS_LRCORNER",   "\342\224\230");
3452
3453     n = SHOW_UTF8(n, "WACS_LTEE",       "\342\224\234");
3454     n = SHOW_UTF8(n, "WACS_RTEE",       "\342\224\244");
3455     n = SHOW_UTF8(n, "WACS_TTEE",       "\342\224\254");
3456     n = SHOW_UTF8(n, "WACS_BTEE",       "\342\224\264");
3457
3458     n = SHOW_UTF8(n, "WACS_HLINE",      "\342\224\200");
3459     n = SHOW_UTF8(n, "WACS_VLINE",      "\342\224\202");
3460
3461     n = SHOW_UTF8(n, "WACS_LARROW",     "\342\206\220");
3462     n = SHOW_UTF8(n, "WACS_RARROW",     "\342\206\222");
3463     n = SHOW_UTF8(n, "WACS_UARROW",     "\342\206\221");
3464     n = SHOW_UTF8(n, "WACS_DARROW",     "\342\206\223");
3465
3466     n = SHOW_UTF8(n, "WACS_BLOCK",      "\342\226\256");
3467     n = SHOW_UTF8(n, "WACS_BOARD",      "\342\226\222");
3468     n = SHOW_UTF8(n, "WACS_LANTERN",    "\342\230\203");
3469     n = SHOW_UTF8(n, "WACS_BULLET",     "\302\267");
3470     n = SHOW_UTF8(n, "WACS_CKBOARD",    "\342\226\222");
3471     n = SHOW_UTF8(n, "WACS_DEGREE",     "\302\260");
3472     n = SHOW_UTF8(n, "WACS_DIAMOND",    "\342\227\206");
3473     n = SHOW_UTF8(n, "WACS_PLMINUS",    "\302\261");
3474     n = SHOW_UTF8(n, "WACS_PLUS",       "\342\224\274");
3475     n = SHOW_UTF8(n, "WACS_GEQUAL",     "\342\211\245");
3476     n = SHOW_UTF8(n, "WACS_NEQUAL",     "\342\211\240");
3477     n = SHOW_UTF8(n, "WACS_LEQUAL",     "\342\211\244");
3478
3479     n = SHOW_UTF8(n, "WACS_STERLING",   "\302\243");
3480     n = SHOW_UTF8(n, "WACS_PI",         "\317\200");
3481     n = SHOW_UTF8(n, "WACS_S1",         "\342\216\272");
3482     n = SHOW_UTF8(n, "WACS_S3",         "\342\216\273");
3483     n = SHOW_UTF8(n, "WACS_S7",         "\342\216\274");
3484     n = SHOW_UTF8(n, "WACS_S9",         "\342\216\275");
3485     /* *INDENT-ON* */
3486
3487 }
3488
3489 /* display the wide-ACS character set */
3490 static void
3491 wide_acs_display(void)
3492 {
3493     int c = 'a';
3494     int digit = 0;
3495     int repeat = 1;
3496     int space = ' ';
3497     chtype attr = A_NORMAL;
3498     int fg = COLOR_BLACK;
3499     int bg = COLOR_BLACK;
3500     unsigned at_code = 0;
3501     short pair = 0;
3502     void (*last_show_wacs) (int, attr_t, short) = 0;
3503
3504     do {
3505         switch (c) {
3506         case CTRL('L'):
3507             Repaint();
3508             break;
3509         case 'a':
3510             ToggleAcs(last_show_wacs, show_wacs_chars);
3511             break;
3512         case 'x':
3513             ToggleAcs(last_show_wacs, show_wbox_chars);
3514             break;
3515         case 'u':
3516             ToggleAcs(last_show_wacs, show_utf8_chars);
3517             break;
3518         default:
3519             if (c < 256 && isdigit(c)) {
3520                 digit = (c - '0');
3521                 last_show_wacs = 0;
3522             } else if (c == '+') {
3523                 ++digit;
3524                 last_show_wacs = 0;
3525             } else if (c == '-' && digit > 0) {
3526                 --digit;
3527                 last_show_wacs = 0;
3528             } else if (c == '>' && repeat < (COLS / 4)) {
3529                 ++repeat;
3530             } else if (c == '<' && repeat > 1) {
3531                 --repeat;
3532             } else if (c == '_') {
3533                 space = (space == ' ') ? '_' : ' ';
3534                 last_show_wacs = 0;
3535             } else if (cycle_attr(c, &at_code, &attr)
3536                        || cycle_colors(c, &fg, &bg, &pair)) {
3537                 if (last_show_wacs != 0)
3538                     break;
3539             } else {
3540                 beep();
3541                 break;
3542             }
3543             break;
3544         }
3545         if (last_show_wacs != 0)
3546             last_show_wacs(repeat, attr, pair);
3547         else
3548             show_upper_widechars(digit * 32 + 128, repeat, space, attr, pair);
3549
3550         mvprintw(LINES - 3, 0,
3551                  "Select: a WACS, x box, u UTF-8, 0-9,+/- non-ASCII, </> repeat, ESC=quit");
3552         if (use_colors) {
3553             mvprintw(LINES - 2, 0,
3554                      "v/V, f/F, b/B cycle through video attributes (%s) and color %d/%d.",
3555                      attrs_to_cycle[at_code].name,
3556                      fg, bg);
3557         } else {
3558             mvprintw(LINES - 2, 0,
3559                      "v/V cycles through video attributes (%s).",
3560                      attrs_to_cycle[at_code].name);
3561         }
3562         refresh();
3563     } while (!isQuit(c = Getchar()));
3564
3565     Pause();
3566     erase();
3567     endwin();
3568 }
3569
3570 #endif
3571
3572 /*
3573  * Graphic-rendition test (adapted from vttest)
3574  */
3575 static void
3576 test_sgr_attributes(void)
3577 {
3578     int pass;
3579
3580     for (pass = 0; pass < 2; pass++) {
3581         chtype normal = ((pass == 0 ? A_NORMAL : A_REVERSE)) | BLANK;
3582
3583         /* Use non-default colors if possible to exercise bce a little */
3584         if (use_colors) {
3585             init_pair(1, COLOR_WHITE, COLOR_BLUE);
3586             normal |= COLOR_PAIR(1);
3587         }
3588         bkgdset(normal);
3589         erase();
3590         mvprintw(1, 20, "Graphic rendition test pattern:");
3591
3592         mvprintw(4, 1, "vanilla");
3593
3594 #define set_sgr(mask) bkgdset((normal^(mask)));
3595         set_sgr(A_BOLD);
3596         mvprintw(4, 40, "bold");
3597
3598         set_sgr(A_UNDERLINE);
3599         mvprintw(6, 6, "underline");
3600
3601         set_sgr(A_BOLD | A_UNDERLINE);
3602         mvprintw(6, 45, "bold underline");
3603
3604         set_sgr(A_BLINK);
3605         mvprintw(8, 1, "blink");
3606
3607         set_sgr(A_BLINK | A_BOLD);
3608         mvprintw(8, 40, "bold blink");
3609
3610         set_sgr(A_UNDERLINE | A_BLINK);
3611         mvprintw(10, 6, "underline blink");
3612
3613         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK);
3614         mvprintw(10, 45, "bold underline blink");
3615
3616         set_sgr(A_REVERSE);
3617         mvprintw(12, 1, "negative");
3618
3619         set_sgr(A_BOLD | A_REVERSE);
3620         mvprintw(12, 40, "bold negative");
3621
3622         set_sgr(A_UNDERLINE | A_REVERSE);
3623         mvprintw(14, 6, "underline negative");
3624
3625         set_sgr(A_BOLD | A_UNDERLINE | A_REVERSE);
3626         mvprintw(14, 45, "bold underline negative");
3627
3628         set_sgr(A_BLINK | A_REVERSE);
3629         mvprintw(16, 1, "blink negative");
3630
3631         set_sgr(A_BOLD | A_BLINK | A_REVERSE);
3632         mvprintw(16, 40, "bold blink negative");
3633
3634         set_sgr(A_UNDERLINE | A_BLINK | A_REVERSE);
3635         mvprintw(18, 6, "underline blink negative");
3636
3637         set_sgr(A_BOLD | A_UNDERLINE | A_BLINK | A_REVERSE);
3638         mvprintw(18, 45, "bold underline blink negative");
3639
3640         bkgdset(normal);
3641         mvprintw(LINES - 2, 1, "%s background. ", pass == 0 ? "Dark" :
3642                  "Light");
3643         clrtoeol();
3644         Pause();
3645     }
3646
3647     bkgdset(A_NORMAL | BLANK);
3648     erase();
3649     endwin();
3650 }
3651
3652 /****************************************************************************
3653  *
3654  * Windows and scrolling tester.
3655  *
3656  ****************************************************************************/
3657
3658 #define BOTLINES        4       /* number of line stolen from screen bottom */
3659
3660 typedef struct {
3661     int y, x;
3662 } pair;
3663
3664 #define FRAME struct frame
3665 FRAME
3666 {
3667     FRAME *next, *last;
3668     bool do_scroll;
3669     bool do_keypad;
3670     WINDOW *wind;
3671 };
3672
3673 #if defined(NCURSES_VERSION)
3674 #if (NCURSES_VERSION_PATCH < 20070331) && NCURSES_EXT_FUNCS
3675 #define is_keypad(win)   (win)->_use_keypad
3676 #define is_scrollok(win) (win)->_scroll
3677 #elif !defined(is_keypad)
3678 #define is_keypad(win)   FALSE
3679 #define is_scrollok(win) FALSE
3680 #endif
3681 #else
3682 #define is_keypad(win)   FALSE
3683 #define is_scrollok(win) FALSE
3684 #endif
3685
3686 static WINDOW *
3687 frame_win(FRAME * curp)
3688 {
3689     return (curp != 0) ? curp->wind : stdscr;
3690 }
3691
3692 /* We need to know if these flags are actually set, so don't look in FRAME.
3693  * These names are known to work with SVr4 curses as well as ncurses.  The
3694  * _use_keypad name does not work with Solaris 8.
3695  */
3696 static bool
3697 HaveKeypad(FRAME * curp)
3698 {
3699     WINDOW *win = frame_win(curp);
3700     (void) win;
3701     return is_keypad(win);
3702 }
3703
3704 static bool
3705 HaveScroll(FRAME * curp)
3706 {
3707     WINDOW *win = frame_win(curp);
3708     (void) win;
3709     return is_scrollok(win);
3710 }
3711
3712 static void
3713 newwin_legend(FRAME * curp)
3714 {
3715     static const struct {
3716         const char *msg;
3717         int code;
3718     } legend[] = {
3719         {
3720             "^C = create window", 0
3721         },
3722         {
3723             "^N = next window", 0
3724         },
3725         {
3726             "^P = previous window", 0
3727         },
3728         {
3729             "^F = scroll forward", 0
3730         },
3731         {
3732             "^B = scroll backward", 0
3733         },
3734         {
3735             "^K = keypad(%s)", 1
3736         },
3737         {
3738             "^S = scrollok(%s)", 2
3739         },
3740         {
3741             "^W = save window to file", 0
3742         },
3743         {
3744             "^R = restore window", 0
3745         },
3746 #if HAVE_WRESIZE
3747         {
3748             "^X = resize", 0
3749         },
3750 #endif
3751         {
3752             "^Q%s = exit", 3
3753         }
3754     };
3755     size_t n;
3756     int x;
3757     bool do_keypad = HaveKeypad(curp);
3758     bool do_scroll = HaveScroll(curp);
3759     char buf[BUFSIZ];
3760
3761     move(LINES - 4, 0);
3762     for (n = 0; n < SIZEOF(legend); n++) {
3763         switch (legend[n].code) {
3764         default:
3765             strcpy(buf, legend[n].msg);
3766             break;
3767         case 1:
3768             sprintf(buf, legend[n].msg, do_keypad ? "yes" : "no");
3769             break;
3770         case 2:
3771             sprintf(buf, legend[n].msg, do_scroll ? "yes" : "no");
3772             break;
3773         case 3:
3774             sprintf(buf, legend[n].msg, do_keypad ? "/ESC" : "");
3775             break;
3776         }
3777         x = getcurx(stdscr);
3778         addstr((COLS < (x + 3 + (int) strlen(buf))) ? "\n" : (n ? ", " : ""));
3779         addstr(buf);
3780     }
3781     clrtoeol();
3782 }
3783
3784 static void
3785 transient(FRAME * curp, NCURSES_CONST char *msg)
3786 {
3787     newwin_legend(curp);
3788     if (msg) {
3789         mvaddstr(LINES - 1, 0, msg);
3790         refresh();
3791         napms(1000);
3792     }
3793
3794     move(LINES - 1, 0);
3795     printw("%s characters are echoed, window should %sscroll.",
3796            HaveKeypad(curp) ? "Non-arrow" : "All other",
3797            HaveScroll(curp) ? "" : "not ");
3798     clrtoeol();
3799 }
3800
3801 static void
3802 newwin_report(FRAME * curp)
3803 /* report on the cursor's current position, then restore it */
3804 {
3805     WINDOW *win = frame_win(curp);
3806     int y, x;
3807
3808     if (win != stdscr)
3809         transient(curp, (char *) 0);
3810     getyx(win, y, x);
3811     move(LINES - 1, COLS - 17);
3812     printw("Y = %2d X = %2d", y, x);
3813     if (win != stdscr)
3814         refresh();
3815     else
3816         wmove(win, y, x);
3817 }
3818
3819 static pair *
3820 selectcell(int uli, int ulj, int lri, int lrj)
3821 /* arrows keys move cursor, return location at current on non-arrow key */
3822 {
3823     static pair res;            /* result cell */
3824     int si = lri - uli + 1;     /* depth of the select area */
3825     int sj = lrj - ulj + 1;     /* width of the select area */
3826     int i = 0, j = 0;           /* offsets into the select area */
3827
3828     res.y = uli;
3829     res.x = ulj;
3830     for (;;) {
3831         move(uli + i, ulj + j);
3832         newwin_report((FRAME *) 0);
3833
3834         switch (Getchar()) {
3835         case KEY_UP:
3836             i += si - 1;
3837             break;
3838         case KEY_DOWN:
3839             i++;
3840             break;
3841         case KEY_LEFT:
3842             j += sj - 1;
3843             break;
3844         case KEY_RIGHT:
3845             j++;
3846             break;
3847         case case_QUIT:
3848             return ((pair *) 0);
3849 #ifdef NCURSES_MOUSE_VERSION
3850         case KEY_MOUSE:
3851             {
3852                 MEVENT event;
3853
3854                 getmouse(&event);
3855                 if (event.y > uli && event.x > ulj) {
3856                     i = event.y - uli;
3857                     j = event.x - ulj;
3858                 } else {
3859                     beep();
3860                     break;
3861                 }
3862             }
3863             /* FALLTHRU */
3864 #endif
3865         default:
3866             res.y = uli + i;
3867             res.x = ulj + j;
3868             return (&res);
3869         }
3870         i %= si;
3871         j %= sj;
3872     }
3873 }
3874
3875 static void
3876 outerbox(pair ul, pair lr, bool onoff)
3877 /* draw or erase a box *outside* the given pair of corners */
3878 {
3879     mvaddch(ul.y - 1, lr.x - 1, onoff ? ACS_ULCORNER : ' ');
3880     mvaddch(ul.y - 1, lr.x + 1, onoff ? ACS_URCORNER : ' ');
3881     mvaddch(lr.y + 1, lr.x + 1, onoff ? ACS_LRCORNER : ' ');
3882     mvaddch(lr.y + 1, ul.x - 1, onoff ? ACS_LLCORNER : ' ');
3883     move(ul.y - 1, ul.x);
3884     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3885     move(ul.y, ul.x - 1);
3886     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3887     move(lr.y + 1, ul.x);
3888     hline(onoff ? ACS_HLINE : ' ', lr.x - ul.x + 1);
3889     move(ul.y, lr.x + 1);
3890     vline(onoff ? ACS_VLINE : ' ', lr.y - ul.y + 1);
3891 }
3892
3893 static WINDOW *
3894 getwindow(void)
3895 /* Ask user for a window definition */
3896 {
3897     WINDOW *rwindow;
3898     pair ul, lr, *tmp;
3899
3900     move(0, 0);
3901     clrtoeol();
3902     addstr("Use arrows to move cursor, anything else to mark corner 1");
3903     refresh();
3904     if ((tmp = selectcell(2, 1, LINES - BOTLINES - 2, COLS - 2)) == (pair *) 0)
3905         return ((WINDOW *) 0);
3906     memcpy(&ul, tmp, sizeof(pair));
3907     mvaddch(ul.y - 1, ul.x - 1, ACS_ULCORNER);
3908     move(0, 0);
3909     clrtoeol();
3910     addstr("Use arrows to move cursor, anything else to mark corner 2");
3911     refresh();
3912     if ((tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2)) ==
3913         (pair *) 0)
3914         return ((WINDOW *) 0);
3915     memcpy(&lr, tmp, sizeof(pair));
3916
3917     rwindow = subwin(stdscr, lr.y - ul.y + 1, lr.x - ul.x + 1, ul.y, ul.x);
3918
3919     outerbox(ul, lr, TRUE);
3920     refresh();
3921
3922     wrefresh(rwindow);
3923
3924     move(0, 0);
3925     clrtoeol();
3926     return (rwindow);
3927 }
3928
3929 static void
3930 newwin_move(FRAME * curp, int dy, int dx)
3931 {
3932     WINDOW *win = frame_win(curp);
3933     int cur_y, cur_x;
3934     int max_y, max_x;
3935
3936     getyx(win, cur_y, cur_x);
3937     getmaxyx(win, max_y, max_x);
3938     if ((cur_x += dx) < 0)
3939         cur_x = 0;
3940     else if (cur_x >= max_x)
3941         cur_x = max_x - 1;
3942     if ((cur_y += dy) < 0)
3943         cur_y = 0;
3944     else if (cur_y >= max_y)
3945         cur_y = max_y - 1;
3946     wmove(win, cur_y, cur_x);
3947 }
3948
3949 static FRAME *
3950 delete_framed(FRAME * fp, bool showit)
3951 {
3952     FRAME *np = 0;
3953
3954     if (fp != 0) {
3955         fp->last->next = fp->next;
3956         fp->next->last = fp->last;
3957
3958         if (showit) {
3959             werase(fp->wind);
3960             wrefresh(fp->wind);
3961         }
3962         delwin(fp->wind);
3963
3964         np = (fp == fp->next) ? 0 : fp->next;
3965         free(fp);
3966     }
3967     return np;
3968 }
3969
3970 static void
3971 acs_and_scroll(void)
3972 /* Demonstrate windows */
3973 {
3974     int c;
3975     FRAME *current = (FRAME *) 0, *neww;
3976     WINDOW *usescr = stdscr;
3977 #if HAVE_PUTWIN && HAVE_GETWIN
3978     FILE *fp;
3979 #endif
3980
3981 #define DUMPFILE        "screendump"
3982
3983 #ifdef NCURSES_MOUSE_VERSION
3984     mousemask(BUTTON1_CLICKED, (mmask_t *) 0);
3985 #endif
3986     c = CTRL('C');
3987     raw();
3988     do {
3989         transient((FRAME *) 0, (char *) 0);
3990         switch (c) {
3991         case CTRL('C'):
3992             if ((neww = typeCalloc(FRAME, 1)) == 0) {
3993                 goto breakout;
3994             }
3995             if ((neww->wind = getwindow()) == (WINDOW *) 0) {
3996                 free(neww);
3997                 goto breakout;
3998             }
3999
4000             if (current == 0) { /* First element,  */
4001                 neww->next = neww;      /*   so point it at itself */
4002                 neww->last = neww;
4003             } else {
4004                 neww->next = current->next;
4005                 neww->last = current;
4006                 neww->last->next = neww;
4007                 neww->next->last = neww;
4008             }
4009             current = neww;
4010             /* SVr4 curses sets the keypad on all newly-created windows to
4011              * false.  Someone reported that PDCurses makes new windows inherit
4012              * this flag.  Remove the following 'keypad()' call to test this
4013              */
4014             keypad(current->wind, TRUE);
4015             current->do_keypad = HaveKeypad(current);
4016             current->do_scroll = HaveScroll(current);
4017             break;
4018
4019         case CTRL('N'): /* go to next window */
4020             if (current)
4021                 current = current->next;
4022             break;
4023
4024         case CTRL('P'): /* go to previous window */
4025             if (current)
4026                 current = current->last;
4027             break;
4028
4029         case CTRL('F'): /* scroll current window forward */
4030             if (current)
4031                 wscrl(frame_win(current), 1);
4032             break;
4033
4034         case CTRL('B'): /* scroll current window backwards */
4035             if (current)
4036                 wscrl(frame_win(current), -1);
4037             break;
4038
4039         case CTRL('K'): /* toggle keypad mode for current */
4040             if (current) {
4041                 current->do_keypad = !current->do_keypad;
4042                 keypad(current->wind, current->do_keypad);
4043             }
4044             break;
4045
4046         case CTRL('S'):
4047             if (current) {
4048                 current->do_scroll = !current->do_scroll;
4049                 scrollok(current->wind, current->do_scroll);
4050             }
4051             break;
4052
4053 #if HAVE_PUTWIN && HAVE_GETWIN
4054         case CTRL('W'): /* save and delete window */
4055             if ((current != 0) && (current == current->next)) {
4056                 transient(current, "Will not save/delete ONLY window");
4057                 break;
4058             } else if ((fp = fopen(DUMPFILE, "w")) == (FILE *) 0) {
4059                 transient(current, "Can't open screen dump file");
4060             } else {
4061                 (void) putwin(frame_win(current), fp);
4062                 (void) fclose(fp);
4063
4064                 current = delete_framed(current, TRUE);
4065             }
4066             break;
4067
4068         case CTRL('R'): /* restore window */
4069             if ((fp = fopen(DUMPFILE, "r")) == (FILE *) 0) {
4070                 transient(current, "Can't open screen dump file");
4071             } else {
4072                 if ((neww = typeCalloc(FRAME, 1)) != 0) {
4073
4074                     neww->next = current ? current->next : 0;
4075                     neww->last = current;
4076                     neww->last->next = neww;
4077                     neww->next->last = neww;
4078
4079                     neww->wind = getwin(fp);
4080
4081                     wrefresh(neww->wind);
4082                 }
4083                 (void) fclose(fp);
4084             }
4085             break;
4086 #endif
4087
4088 #if HAVE_WRESIZE
4089         case CTRL('X'): /* resize window */
4090             if (current) {
4091                 pair *tmp, ul, lr;
4092                 int i, mx, my;
4093
4094                 move(0, 0);
4095                 clrtoeol();
4096                 addstr("Use arrows to move cursor, anything else to mark new corner");
4097                 refresh();
4098
4099                 getbegyx(current->wind, ul.y, ul.x);
4100
4101                 tmp = selectcell(ul.y, ul.x, LINES - BOTLINES - 2, COLS - 2);
4102                 if (tmp == (pair *) 0) {
4103                     beep();
4104                     break;
4105                 }
4106
4107                 getmaxyx(current->wind, lr.y, lr.x);
4108                 lr.y += (ul.y - 1);
4109                 lr.x += (ul.x - 1);
4110                 outerbox(ul, lr, FALSE);
4111                 wnoutrefresh(stdscr);
4112
4113                 /* strictly cosmetic hack for the test */
4114                 getmaxyx(current->wind, my, mx);
4115                 if (my > tmp->y - ul.y) {
4116                     getyx(current->wind, lr.y, lr.x);
4117                     wmove(current->wind, tmp->y - ul.y + 1, 0);
4118                     wclrtobot(current->wind);
4119                     wmove(current->wind, lr.y, lr.x);
4120                 }
4121                 if (mx > tmp->x - ul.x)
4122                     for (i = 0; i < my; i++) {
4123                         wmove(current->wind, i, tmp->x - ul.x + 1);
4124                         wclrtoeol(current->wind);
4125                     }
4126                 wnoutrefresh(current->wind);
4127
4128                 memcpy(&lr, tmp, sizeof(pair));
4129                 (void) wresize(current->wind, lr.y - ul.y + 0, lr.x - ul.x + 0);
4130
4131                 getbegyx(current->wind, ul.y, ul.x);
4132                 getmaxyx(current->wind, lr.y, lr.x);
4133                 lr.y += (ul.y - 1);
4134                 lr.x += (ul.x - 1);
4135                 outerbox(ul, lr, TRUE);
4136                 wnoutrefresh(stdscr);
4137
4138                 wnoutrefresh(current->wind);
4139                 move(0, 0);
4140                 clrtoeol();
4141                 doupdate();
4142             }
4143             break;
4144 #endif /* HAVE_WRESIZE */
4145
4146         case KEY_F(10): /* undocumented --- use this to test area clears */
4147             selectcell(0, 0, LINES - 1, COLS - 1);
4148             clrtobot();
4149             refresh();
4150             break;
4151
4152         case KEY_UP:
4153             newwin_move(current, -1, 0);
4154             break;
4155         case KEY_DOWN:
4156             newwin_move(current, 1, 0);
4157             break;
4158         case KEY_LEFT:
4159             newwin_move(current, 0, -1);
4160             break;
4161         case KEY_RIGHT:
4162             newwin_move(current, 0, 1);
4163             break;
4164
4165         case KEY_BACKSPACE:
4166             /* FALLTHROUGH */
4167         case KEY_DC:
4168             {
4169                 int y, x;
4170                 getyx(frame_win(current), y, x);
4171                 if (--x < 0) {
4172                     if (--y < 0)
4173                         break;
4174                     x = getmaxx(frame_win(current)) - 1;
4175                 }
4176                 mvwdelch(frame_win(current), y, x);
4177             }
4178             break;
4179
4180         case '\r':
4181             c = '\n';
4182             /* FALLTHROUGH */
4183
4184         default:
4185             if (current)
4186                 waddch(current->wind, (chtype) c);
4187             else
4188                 beep();
4189             break;
4190         }
4191         newwin_report(current);
4192         usescr = frame_win(current);
4193         wrefresh(usescr);
4194     } while
4195         (!isQuit(c = wGetchar(usescr))
4196          && (c != ERR));
4197
4198   breakout:
4199     while (current != 0)
4200         current = delete_framed(current, FALSE);
4201
4202     scrollok(stdscr, TRUE);     /* reset to driver's default */
4203 #ifdef NCURSES_MOUSE_VERSION
4204     mousemask(0, (mmask_t *) 0);
4205 #endif
4206     noraw();
4207     erase();
4208     endwin();
4209 }
4210
4211 /****************************************************************************
4212  *
4213  * Panels tester
4214  *
4215  ****************************************************************************/
4216
4217 #if USE_LIBPANEL
4218 static int nap_msec = 1;
4219
4220 static NCURSES_CONST char *mod[] =
4221 {
4222     "test ",
4223     "TEST ",
4224     "(**) ",
4225     "*()* ",
4226     "<--> ",
4227     "LAST "
4228 };
4229
4230 /*+-------------------------------------------------------------------------
4231         wait_a_while(msec)
4232 --------------------------------------------------------------------------*/
4233 static void
4234 wait_a_while(int msec GCC_UNUSED)
4235 {
4236 #if HAVE_NAPMS
4237     if (nap_msec == 1)
4238         wGetchar(stdscr);
4239     else
4240         napms(nap_msec);
4241 #else
4242     if (nap_msec == 1)
4243         wGetchar(stdscr);
4244     else if (msec > 1000)
4245         sleep((unsigned) msec / 1000);
4246     else
4247         sleep(1);
4248 #endif
4249 }                               /* end of wait_a_while */
4250
4251 /*+-------------------------------------------------------------------------
4252         saywhat(text)
4253 --------------------------------------------------------------------------*/
4254 static void
4255 saywhat(NCURSES_CONST char *text)
4256 {
4257     wmove(stdscr, LINES - 1, 0);
4258     wclrtoeol(stdscr);
4259     if (text != 0 && *text != '\0') {
4260         waddstr(stdscr, text);
4261         waddstr(stdscr, "; ");
4262     }
4263     waddstr(stdscr, "press any key to continue");
4264 }                               /* end of saywhat */
4265
4266 /*+-------------------------------------------------------------------------
4267         mkpanel(rows,cols,tly,tlx) - alloc a win and panel and associate them
4268 --------------------------------------------------------------------------*/
4269 static PANEL *
4270 mkpanel(short color, int rows, int cols, int tly, int tlx)
4271 {
4272     WINDOW *win;
4273     PANEL *pan = 0;
4274
4275     if ((win = newwin(rows, cols, tly, tlx)) != 0) {
4276         if ((pan = new_panel(win)) == 0) {
4277             delwin(win);
4278         } else if (use_colors) {
4279             short fg = (short) ((color == COLOR_BLUE) ? COLOR_WHITE : COLOR_BLACK);
4280             short bg = color;
4281
4282             init_pair(color, fg, bg);
4283             wbkgdset(win, (chtype) (COLOR_PAIR(color) | ' '));
4284         } else {
4285             wbkgdset(win, A_BOLD | ' ');
4286         }
4287     }
4288     return pan;
4289 }                               /* end of mkpanel */
4290
4291 /*+-------------------------------------------------------------------------
4292         rmpanel(pan)
4293 --------------------------------------------------------------------------*/
4294 static void
4295 rmpanel(PANEL * pan)
4296 {
4297     WINDOW *win = panel_window(pan);
4298     del_panel(pan);
4299     delwin(win);
4300 }                               /* end of rmpanel */
4301
4302 /*+-------------------------------------------------------------------------
4303         pflush()
4304 --------------------------------------------------------------------------*/
4305 static void
4306 pflush(void)
4307 {
4308     update_panels();
4309     doupdate();
4310 }                               /* end of pflush */
4311
4312 /*+-------------------------------------------------------------------------
4313         fill_panel(win)
4314 --------------------------------------------------------------------------*/
4315 static void
4316 init_panel(void)
4317 {
4318     register int y, x;
4319
4320     for (y = 0; y < LINES - 1; y++) {
4321         for (x = 0; x < COLS; x++)
4322             wprintw(stdscr, "%d", (y + x) % 10);
4323     }
4324 }
4325
4326 static void
4327 fill_panel(PANEL * pan)
4328 {
4329     WINDOW *win = panel_window(pan);
4330     const char *userptr = (const char *) panel_userptr(pan);
4331     int num = (userptr && *userptr) ? userptr[1] : '?';
4332     int y, x;
4333
4334     wmove(win, 1, 1);
4335     wprintw(win, "-pan%c-", num);
4336     wclrtoeol(win);
4337     box(win, 0, 0);
4338     for (y = 2; y < getmaxy(win) - 1; y++) {
4339         for (x = 1; x < getmaxx(win) - 1; x++) {
4340             wmove(win, y, x);
4341             waddch(win, UChar(num));
4342         }
4343     }
4344 }
4345
4346 #if USE_WIDEC_SUPPORT
4347 static void
4348 init_wide_panel(void)
4349 {
4350     int digit;
4351     cchar_t temp[10];
4352
4353     for (digit = 0; digit < 10; ++digit)
4354         make_fullwidth_digit(&temp[digit], digit);
4355
4356     do {
4357         int y, x;
4358         getyx(stdscr, y, x);
4359         digit = (y + x / 2) % 10;
4360     } while (add_wch(&temp[digit]) != ERR);
4361 }
4362
4363 static void
4364 fill_wide_panel(PANEL * pan)
4365 {
4366     WINDOW *win = panel_window(pan);
4367     const char *userptr = (const char *) panel_userptr(pan);
4368     int num = (userptr && *userptr) ? userptr[1] : '?';
4369     int y, x;
4370
4371     wmove(win, 1, 1);
4372     wprintw(win, "-pan%c-", num);
4373     wclrtoeol(win);
4374     box(win, 0, 0);
4375     for (y = 2; y < getmaxy(win) - 1; y++) {
4376         for (x = 1; x < getmaxx(win) - 1; x++) {
4377             wmove(win, y, x);
4378             waddch(win, UChar(num));
4379         }
4380     }
4381 }
4382 #endif
4383
4384 #define MAX_PANELS 5
4385
4386 static void
4387 canned_panel(PANEL * px[MAX_PANELS + 1], NCURSES_CONST char *cmd)
4388 {
4389     int which = cmd[1] - '0';
4390
4391     saywhat(cmd);
4392     switch (*cmd) {
4393     case 'h':
4394         hide_panel(px[which]);
4395         break;
4396     case 's':
4397         show_panel(px[which]);
4398         break;
4399     case 't':
4400         top_panel(px[which]);
4401         break;
4402     case 'b':
4403         bottom_panel(px[which]);
4404         break;
4405     case 'd':
4406         rmpanel(px[which]);
4407         break;
4408     }
4409     pflush();
4410     wait_a_while(nap_msec);
4411 }
4412
4413 static void
4414 demo_panels(void (*InitPanel) (void), void (*FillPanel) (PANEL *))
4415 {
4416     int count;
4417     int itmp;
4418     PANEL *px[MAX_PANELS + 1];
4419
4420     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4421     refresh();
4422
4423     InitPanel();
4424     for (count = 0; count < 5; count++) {
4425         px[1] = mkpanel(COLOR_RED,
4426                         LINES / 2 - 2,
4427                         COLS / 8 + 1,
4428                         0,
4429                         0);
4430         set_panel_userptr(px[1], (NCURSES_CONST void *) "p1");
4431
4432         px[2] = mkpanel(COLOR_GREEN,
4433                         LINES / 2 + 1,
4434                         COLS / 7,
4435                         LINES / 4,
4436                         COLS / 10);
4437         set_panel_userptr(px[2], (NCURSES_CONST void *) "p2");
4438
4439         px[3] = mkpanel(COLOR_YELLOW,
4440                         LINES / 4,
4441                         COLS / 10,
4442                         LINES / 2,
4443                         COLS / 9);
4444         set_panel_userptr(px[3], (NCURSES_CONST void *) "p3");
4445
4446         px[4] = mkpanel(COLOR_BLUE,
4447                         LINES / 2 - 2,
4448                         COLS / 8,
4449                         LINES / 2 - 2,
4450                         COLS / 3);
4451         set_panel_userptr(px[4], (NCURSES_CONST void *) "p4");
4452
4453         px[5] = mkpanel(COLOR_MAGENTA,
4454                         LINES / 2 - 2,
4455                         COLS / 8,
4456                         LINES / 2,
4457                         COLS / 2 - 2);
4458         set_panel_userptr(px[5], (NCURSES_CONST void *) "p5");
4459
4460         FillPanel(px[1]);
4461         FillPanel(px[2]);
4462         FillPanel(px[3]);
4463         FillPanel(px[4]);
4464         FillPanel(px[5]);
4465
4466         hide_panel(px[4]);
4467         hide_panel(px[5]);
4468         pflush();
4469         saywhat("");
4470         wait_a_while(nap_msec);
4471
4472         saywhat("h3 s1 s2 s4 s5");
4473         move_panel(px[1], 0, 0);
4474         hide_panel(px[3]);
4475         show_panel(px[1]);
4476         show_panel(px[2]);
4477         show_panel(px[4]);
4478         show_panel(px[5]);
4479         pflush();
4480         wait_a_while(nap_msec);
4481
4482         canned_panel(px, "s1");
4483         canned_panel(px, "s2");
4484
4485         saywhat("m2");
4486         move_panel(px[2], LINES / 3 + 1, COLS / 8);
4487         pflush();
4488         wait_a_while(nap_msec);
4489
4490         canned_panel(px, "s3");
4491
4492         saywhat("m3");
4493         move_panel(px[3], LINES / 4 + 1, COLS / 15);
4494         pflush();
4495         wait_a_while(nap_msec);
4496
4497         canned_panel(px, "b3");
4498         canned_panel(px, "s4");
4499         canned_panel(px, "s5");
4500         canned_panel(px, "t3");
4501         canned_panel(px, "t1");
4502         canned_panel(px, "t2");
4503         canned_panel(px, "t3");
4504         canned_panel(px, "t4");
4505
4506         for (itmp = 0; itmp < 6; itmp++) {
4507             WINDOW *w4 = panel_window(px[4]);
4508             WINDOW *w5 = panel_window(px[5]);
4509
4510             saywhat("m4");
4511             wmove(w4, LINES / 8, 1);
4512             waddstr(w4, mod[itmp]);
4513             move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4514             wmove(w5, LINES / 6, 1);
4515             waddstr(w5, mod[itmp]);
4516             pflush();
4517             wait_a_while(nap_msec);
4518
4519             saywhat("m5");
4520             wmove(w4, LINES / 6, 1);
4521             waddstr(w4, mod[itmp]);
4522             move_panel(px[5], LINES / 3 - 1, (itmp * 10) + 6);
4523             wmove(w5, LINES / 8, 1);
4524             waddstr(w5, mod[itmp]);
4525             pflush();
4526             wait_a_while(nap_msec);
4527         }
4528
4529         saywhat("m4");
4530         move_panel(px[4], LINES / 6, itmp * (COLS / 8));
4531         pflush();
4532         wait_a_while(nap_msec);
4533
4534         canned_panel(px, "t5");
4535         canned_panel(px, "t2");
4536         canned_panel(px, "t1");
4537         canned_panel(px, "d2");
4538         canned_panel(px, "h3");
4539         canned_panel(px, "d1");
4540         canned_panel(px, "d4");
4541         canned_panel(px, "d5");
4542         canned_panel(px, "d3");
4543
4544         wait_a_while(nap_msec);
4545         if (nap_msec == 1)
4546             break;
4547         nap_msec = 100L;
4548     }
4549
4550     erase();
4551     endwin();
4552 }
4553 #endif /* USE_LIBPANEL */
4554
4555 /****************************************************************************
4556  *
4557  * Pad tester
4558  *
4559  ****************************************************************************/
4560
4561 #define GRIDSIZE        3
4562
4563 static bool pending_pan = FALSE;
4564 static bool show_panner_legend = TRUE;
4565
4566 static int
4567 panner_legend(int line)
4568 {
4569     static const char *const legend[] =
4570     {
4571         "Use arrow keys (or U,D,L,R) to pan, ESC to quit, ! to shell-out.",
4572         "Use +,- (or j,k) to grow/shrink the panner vertically.",
4573         "Use <,> (or h,l) to grow/shrink the panner horizontally.",
4574         "Number repeats.  Toggle legend:? filler:a timer:t scrollmark:s."
4575     };
4576     int n = ((int) SIZEOF(legend) - (LINES - line));
4577     if (line < LINES && (n >= 0)) {
4578         move(line, 0);
4579         if (show_panner_legend)
4580             printw("%s", legend[n]);
4581         clrtoeol();
4582         return show_panner_legend;
4583     }
4584     return FALSE;
4585 }
4586
4587 static void
4588 panner_h_cleanup(int from_y, int from_x, int to_x)
4589 {
4590     if (!panner_legend(from_y))
4591         do_h_line(from_y, from_x, ' ', to_x);
4592 }
4593
4594 static void
4595 panner_v_cleanup(int from_y, int from_x, int to_y)
4596 {
4597     if (!panner_legend(from_y))
4598         do_v_line(from_y, from_x, ' ', to_y);
4599 }
4600
4601 static void
4602 fill_pad(WINDOW *panpad, bool pan_lines)
4603 {
4604     int y, x;
4605     unsigned gridcount = 0;
4606
4607     wmove(panpad, 0, 0);
4608     for (y = 0; y < getmaxy(panpad); y++) {
4609         for (x = 0; x < getmaxx(panpad); x++) {
4610             if (y % GRIDSIZE == 0 && x % GRIDSIZE == 0) {
4611                 if (y == 0 && x == 0)
4612                     waddch(panpad, pan_lines ? ACS_ULCORNER : '+');
4613                 else if (y == 0)
4614                     waddch(panpad, pan_lines ? ACS_TTEE : '+');
4615                 else if (y == 0 || x == 0)
4616                     waddch(panpad, pan_lines ? ACS_LTEE : '+');
4617                 else
4618                     waddch(panpad, (chtype) ((pan_lines ? 'a' : 'A') +
4619                                              (int) (gridcount++ % 26)));
4620             } else if (y % GRIDSIZE == 0)
4621                 waddch(panpad, pan_lines ? ACS_HLINE : '-');
4622             else if (x % GRIDSIZE == 0)
4623                 waddch(panpad, pan_lines ? ACS_VLINE : '|');
4624             else
4625                 waddch(panpad, ' ');
4626         }
4627     }
4628 }
4629
4630 static void
4631 panner(WINDOW *pad,
4632        int top_x, int top_y, int porty, int portx,
4633        int (*pgetc) (WINDOW *))
4634 {
4635 #if HAVE_GETTIMEOFDAY
4636     struct timeval before, after;
4637     bool timing = TRUE;
4638 #endif
4639     bool pan_lines = FALSE;
4640     bool scrollers = TRUE;
4641     int basex = 0;
4642     int basey = 0;
4643     int pxmax, pymax, lowend, highend, c;
4644
4645     getmaxyx(pad, pymax, pxmax);
4646     scrollok(stdscr, FALSE);    /* we don't want stdscr to scroll! */
4647
4648     c = KEY_REFRESH;
4649     do {
4650 #ifdef NCURSES_VERSION
4651         /*
4652          * During shell-out, the user may have resized the window.  Adjust
4653          * the port size of the pad to accommodate this.  Ncurses automatically
4654          * resizes all of the normal windows to fit on the new screen.
4655          */
4656         if (top_x > COLS)
4657             top_x = COLS;
4658         if (portx > COLS)
4659             portx = COLS;
4660         if (top_y > LINES)
4661             top_y = LINES;
4662         if (porty > LINES)
4663             porty = LINES;
4664 #endif
4665         switch (c) {
4666         case KEY_REFRESH:
4667             erase();
4668
4669             /* FALLTHRU */
4670         case '?':
4671             if (c == '?')
4672                 show_panner_legend = !show_panner_legend;
4673             panner_legend(LINES - 4);
4674             panner_legend(LINES - 3);
4675             panner_legend(LINES - 2);
4676             panner_legend(LINES - 1);
4677             break;
4678         case 'a':
4679             pan_lines = !pan_lines;
4680             fill_pad(pad, pan_lines);
4681             pending_pan = FALSE;
4682             break;
4683
4684 #if HAVE_GETTIMEOFDAY
4685         case 't':
4686             timing = !timing;
4687             if (!timing)
4688                 panner_legend(LINES - 1);
4689             break;
4690 #endif
4691         case 's':
4692             scrollers = !scrollers;
4693             break;
4694
4695             /* Move the top-left corner of the pad, keeping the bottom-right
4696              * corner fixed.
4697              */
4698         case 'h':               /* increase-columns: move left edge to left */
4699             if (top_x <= 0)
4700                 beep();
4701             else {
4702                 panner_v_cleanup(top_y, top_x, porty);
4703                 top_x--;
4704             }
4705             break;
4706
4707         case 'j':               /* decrease-lines: move top-edge down */
4708             if (top_y >= porty)
4709                 beep();
4710             else {
4711                 panner_h_cleanup(top_y - 1, top_x - (top_x > 0), portx);
4712                 top_y++;
4713             }
4714             break;
4715
4716         case 'k':               /* increase-lines: move top-edge up */
4717             if (top_y <= 0)
4718                 beep();
4719             else {
4720                 top_y--;
4721                 panner_h_cleanup(top_y, top_x, portx);
4722             }
4723             break;
4724
4725         case 'l':               /* decrease-columns: move left-edge to right */
4726             if (top_x >= portx)
4727                 beep();
4728             else {
4729                 panner_v_cleanup(top_y - (top_y > 0), top_x - 1, porty);
4730                 top_x++;
4731             }
4732             break;
4733
4734             /* Move the bottom-right corner of the pad, keeping the top-left
4735              * corner fixed.
4736              */
4737         case KEY_IC:            /* increase-columns: move right-edge to right */
4738             if (portx >= pxmax || portx >= COLS)
4739                 beep();
4740             else {
4741                 panner_v_cleanup(top_y - (top_y > 0), portx - 1, porty);
4742                 ++portx;
4743             }
4744             break;
4745
4746         case KEY_IL:            /* increase-lines: move bottom-edge down */
4747             if (porty >= pymax || porty >= LINES)
4748                 beep();
4749             else {
4750                 panner_h_cleanup(porty - 1, top_x - (top_x > 0), portx);
4751                 ++porty;
4752             }
4753             break;
4754
4755         case KEY_DC:            /* decrease-columns: move bottom edge up */
4756             if (portx <= top_x)
4757                 beep();
4758             else {
4759                 portx--;
4760                 panner_v_cleanup(top_y - (top_y > 0), portx, porty);
4761             }
4762             break;
4763
4764         case KEY_DL:            /* decrease-lines */
4765             if (porty <= top_y)
4766                 beep();
4767             else {
4768                 porty--;
4769                 panner_h_cleanup(porty, top_x - (top_x > 0), portx);
4770             }
4771             break;
4772
4773         case KEY_LEFT:          /* pan leftwards */
4774             if (basex > 0)
4775                 basex--;
4776             else
4777                 beep();
4778             break;
4779
4780         case KEY_RIGHT: /* pan rightwards */
4781             if (basex + portx - (pymax > porty) < pxmax)
4782                 basex++;
4783             else
4784                 beep();
4785             break;
4786
4787         case KEY_UP:            /* pan upwards */
4788             if (basey > 0)
4789                 basey--;
4790             else
4791                 beep();
4792             break;
4793
4794         case KEY_DOWN:          /* pan downwards */
4795             if (basey + porty - (pxmax > portx) < pymax)
4796                 basey++;
4797             else
4798                 beep();
4799             break;
4800
4801         case 'H':
4802         case KEY_HOME:
4803         case KEY_FIND:
4804             basey = 0;
4805             break;
4806
4807         case 'E':
4808         case KEY_END:
4809         case KEY_SELECT:
4810             basey = pymax - porty;
4811             if (basey < 0)
4812                 basey = 0;
4813             break;
4814
4815         default:
4816             beep();
4817             break;
4818         }
4819
4820         mvaddch(top_y - 1, top_x - 1, ACS_ULCORNER);
4821         do_v_line(top_y, top_x - 1, ACS_VLINE, porty);
4822         do_h_line(top_y - 1, top_x, ACS_HLINE, portx);
4823
4824         if (scrollers && (pxmax > portx - 1)) {
4825             int length = (portx - top_x - 1);
4826             float ratio = ((float) length) / ((float) pxmax);
4827
4828             lowend = (int) ((float) top_x + ((float) basex * ratio));
4829             highend = (int) ((float) top_x + ((float) (basex + length) * ratio));
4830
4831             do_h_line(porty - 1, top_x, ACS_HLINE, lowend);
4832             if (highend < portx) {
4833                 attron(A_REVERSE);
4834                 do_h_line(porty - 1, lowend, ' ', highend + 1);
4835                 attroff(A_REVERSE);
4836                 do_h_line(porty - 1, highend + 1, ACS_HLINE, portx);
4837             }
4838         } else
4839             do_h_line(porty - 1, top_x, ACS_HLINE, portx);
4840
4841         if (scrollers && (pymax > porty - 1)) {
4842             int length = (porty - top_y - 1);
4843             float ratio = ((float) length) / ((float) pymax);
4844
4845             lowend = (int) ((float) top_y + ((float) basey * ratio));
4846             highend = (int) ((float) top_y + ((float) (basey + length) * ratio));
4847
4848             do_v_line(top_y, portx - 1, ACS_VLINE, lowend);
4849             if (highend < porty) {
4850                 attron(A_REVERSE);
4851                 do_v_line(lowend, portx - 1, ' ', highend + 1);
4852                 attroff(A_REVERSE);
4853                 do_v_line(highend + 1, portx - 1, ACS_VLINE, porty);
4854             }
4855         } else
4856             do_v_line(top_y, portx - 1, ACS_VLINE, porty);
4857
4858         mvaddch(top_y - 1, portx - 1, ACS_URCORNER);
4859         mvaddch(porty - 1, top_x - 1, ACS_LLCORNER);
4860         mvaddch(porty - 1, portx - 1, ACS_LRCORNER);
4861
4862         if (!pending_pan) {
4863 #if HAVE_GETTIMEOFDAY
4864             gettimeofday(&before, 0);
4865 #endif
4866             wnoutrefresh(stdscr);
4867
4868             pnoutrefresh(pad,
4869                          basey, basex,
4870                          top_y, top_x,
4871                          porty - (pxmax > portx) - 1,
4872                          portx - (pymax > porty) - 1);
4873
4874             doupdate();
4875 #if HAVE_GETTIMEOFDAY
4876             if (timing) {
4877                 double elapsed;
4878                 gettimeofday(&after, 0);
4879                 elapsed = (after.tv_sec + after.tv_usec / 1.0e6)
4880                     - (before.tv_sec + before.tv_usec / 1.0e6);
4881                 move(LINES - 1, COLS - 12);
4882                 printw("Secs: %2.03f", elapsed);
4883                 refresh();
4884             }
4885 #endif
4886         }
4887
4888     } while
4889         ((c = pgetc(pad)) != KEY_EXIT);
4890
4891     scrollok(stdscr, TRUE);     /* reset to driver's default */
4892 }
4893
4894 static int
4895 padgetch(WINDOW *win)
4896 {
4897     static int count;
4898     static int last;
4899     int c;
4900
4901     if ((pending_pan = (count > 0)) != FALSE) {
4902         count--;
4903         pending_pan = (count != 0);
4904     } else {
4905         for (;;) {
4906             switch (c = wGetchar(win)) {
4907             case '!':
4908                 ShellOut(FALSE);
4909                 /* FALLTHRU */
4910             case CTRL('r'):
4911                 endwin();
4912                 refresh();
4913                 c = KEY_REFRESH;
4914                 break;
4915             case CTRL('l'):
4916                 c = KEY_REFRESH;
4917                 break;
4918             case 'U':
4919                 c = KEY_UP;
4920                 break;
4921             case 'D':
4922                 c = KEY_DOWN;
4923                 break;
4924             case 'R':
4925                 c = KEY_RIGHT;
4926                 break;
4927             case 'L':
4928                 c = KEY_LEFT;
4929                 break;
4930             case '+':
4931                 c = KEY_IL;
4932                 break;
4933             case '-':
4934                 c = KEY_DL;
4935                 break;
4936             case '>':
4937                 c = KEY_IC;
4938                 break;
4939             case '<':
4940                 c = KEY_DC;
4941                 break;
4942             case ERR:           /* FALLTHRU */
4943             case case_QUIT:
4944                 count = 0;
4945                 c = KEY_EXIT;
4946                 break;
4947             default:
4948                 if (c >= '0' && c <= '9') {
4949                     count = count * 10 + (c - '0');
4950                     continue;
4951                 }
4952                 break;
4953             }
4954             last = c;
4955             break;
4956         }
4957         if (count > 0)
4958             count--;
4959     }
4960     return (last);
4961 }
4962
4963 #define PAD_HIGH 200
4964 #define PAD_WIDE 200
4965
4966 static void
4967 demo_pad(void)
4968 /* Demonstrate pads. */
4969 {
4970     WINDOW *panpad = newpad(PAD_HIGH, PAD_WIDE);
4971
4972     if (panpad == 0) {
4973         Cannot("cannot create requested pad");
4974         return;
4975     }
4976
4977     fill_pad(panpad, FALSE);
4978
4979     panner_legend(LINES - 4);
4980     panner_legend(LINES - 3);
4981     panner_legend(LINES - 2);
4982     panner_legend(LINES - 1);
4983
4984     keypad(panpad, TRUE);
4985
4986     /* Make the pad (initially) narrow enough that a trace file won't wrap.
4987      * We'll still be able to widen it during a test, since that's required
4988      * for testing boundaries.
4989      */
4990     panner(panpad, 2, 2, LINES - 5, COLS - 15, padgetch);
4991
4992     delwin(panpad);
4993     endwin();
4994     erase();
4995 }
4996
4997 /****************************************************************************
4998  *
4999  * Tests from John Burnell's PDCurses tester
5000  *
5001  ****************************************************************************/
5002
5003 static void
5004 Continue(WINDOW *win)
5005 {
5006     noecho();
5007     wmove(win, 10, 1);
5008     mvwaddstr(win, 10, 1, " Press any key to continue");
5009     wrefresh(win);
5010     wGetchar(win);
5011 }
5012
5013 static void
5014 flushinp_test(WINDOW *win)
5015 /* Input test, adapted from John Burnell's PDCurses tester */
5016 {
5017     int w, h, bx, by, sw, sh, i;
5018
5019     WINDOW *subWin;
5020     wclear(win);
5021
5022     getmaxyx(win, h, w);
5023     getbegyx(win, by, bx);
5024     sw = w / 3;
5025     sh = h / 3;
5026     if ((subWin = subwin(win, sh, sw, by + h - sh - 2, bx + w - sw - 2)) == 0)
5027         return;
5028
5029 #ifdef A_COLOR
5030     if (use_colors) {
5031         init_pair(2, COLOR_CYAN, COLOR_BLUE);
5032         wbkgd(subWin, COLOR_PAIR(2) | ' ');
5033     }
5034 #endif
5035     wattrset(subWin, A_BOLD);
5036     box(subWin, ACS_VLINE, ACS_HLINE);
5037     mvwaddstr(subWin, 2, 1, "This is a subwindow");
5038     wrefresh(win);
5039
5040     /*
5041      * This used to set 'nocbreak()'.  However, Alexander Lukyanov says that
5042      * it only happened to "work" on SVr4 because that implementation does not
5043      * emulate nocbreak+noecho mode, whereas ncurses does.  To get the desired
5044      * test behavior, we're using 'cbreak()', which will allow a single
5045      * character to return without needing a newline. - T.Dickey 1997/10/11.
5046      */
5047     cbreak();
5048     mvwaddstr(win, 0, 1, "This is a test of the flushinp() call.");
5049
5050     mvwaddstr(win, 2, 1, "Type random keys for 5 seconds.");
5051     mvwaddstr(win, 3, 1,
5052               "These should be discarded (not echoed) after the subwindow goes away.");
5053     wrefresh(win);
5054
5055     for (i = 0; i < 5; i++) {
5056         mvwprintw(subWin, 1, 1, "Time = %d", i);
5057         wrefresh(subWin);
5058         napms(1000);
5059         flushinp();
5060     }
5061
5062     delwin(subWin);
5063     werase(win);
5064     flash();
5065     wrefresh(win);
5066     napms(1000);
5067
5068     mvwaddstr(win, 2, 1,
5069               "If you were still typing when the window timer expired,");
5070     mvwaddstr(win, 3, 1,
5071               "or else you typed nothing at all while it was running,");
5072     mvwaddstr(win, 4, 1,
5073               "test was invalid.  You'll see garbage or nothing at all. ");
5074     mvwaddstr(win, 6, 1, "Press a key");
5075     wmove(win, 9, 10);
5076     wrefresh(win);
5077     echo();
5078     wGetchar(win);
5079     flushinp();
5080     mvwaddstr(win, 12, 0,
5081               "If you see any key other than what you typed, flushinp() is broken.");
5082     Continue(win);
5083
5084     wmove(win, 9, 10);
5085     wdelch(win);
5086     wrefresh(win);
5087     wmove(win, 12, 0);
5088     clrtoeol();
5089     waddstr(win,
5090             "What you typed should now have been deleted; if not, wdelch() failed.");
5091     Continue(win);
5092
5093     cbreak();
5094 }
5095
5096 /****************************************************************************
5097  *
5098  * Menu test
5099  *
5100  ****************************************************************************/
5101
5102 #if USE_LIBMENU
5103
5104 #define MENU_Y  8
5105 #define MENU_X  8
5106
5107 static int
5108 menu_virtualize(int c)
5109 {
5110     if (c == '\n' || c == KEY_EXIT)
5111         return (MAX_COMMAND + 1);
5112     else if (c == 'u')
5113         return (REQ_SCR_ULINE);
5114     else if (c == 'd')
5115         return (REQ_SCR_DLINE);
5116     else if (c == 'b' || c == KEY_NPAGE)
5117         return (REQ_SCR_UPAGE);
5118     else if (c == 'f' || c == KEY_PPAGE)
5119         return (REQ_SCR_DPAGE);
5120     else if (c == 'n' || c == KEY_DOWN)
5121         return (REQ_NEXT_ITEM);
5122     else if (c == 'p' || c == KEY_UP)
5123         return (REQ_PREV_ITEM);
5124     else if (c == ' ')
5125         return (REQ_TOGGLE_ITEM);
5126     else {
5127         if (c != KEY_MOUSE)
5128             beep();
5129         return (c);
5130     }
5131 }
5132
5133 static CONST_MENUS char *animals[] =
5134 {
5135     "Lions",
5136     "Tigers",
5137     "Bears",
5138     "(Oh my!)",
5139     "Newts",
5140     "Platypi",
5141     "Lemurs",
5142     "(Oh really?!)",
5143     "Leopards",
5144     "Panthers",
5145     "Pumas",
5146     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5147     "Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs, Lions, Tigers, Bears, (Oh my!), Newts, Platypi, Lemurs",
5148     (char *) 0
5149 };
5150
5151 static void
5152 menu_test(void)
5153 {
5154     MENU *m;
5155     ITEM *items[SIZEOF(animals)];
5156     ITEM **ip = items;
5157     CONST_MENUS char **ap;
5158     int mrows, mcols, c;
5159     WINDOW *menuwin;
5160
5161 #ifdef NCURSES_MOUSE_VERSION
5162     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5163 #endif
5164     mvaddstr(0, 0, "This is the menu test:");
5165     mvaddstr(2, 0, "  Use up and down arrow to move the select bar.");
5166     mvaddstr(3, 0, "  'n' and 'p' act like arrows.");
5167     mvaddstr(4, 0,
5168              "  'b' and 'f' scroll up/down (page), 'u' and 'd' (line).");
5169     mvaddstr(5, 0, "  Press return to exit.");
5170     refresh();
5171
5172     for (ap = animals; *ap; ap++) {
5173         if ((*ip = new_item(*ap, "")) != 0)
5174             ++ip;
5175     }
5176     *ip = (ITEM *) 0;
5177
5178     m = new_menu(items);
5179
5180     set_menu_format(m, (SIZEOF(animals) + 1) / 2, 1);
5181     scale_menu(m, &mrows, &mcols);
5182
5183     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5184     set_menu_win(m, menuwin);
5185     keypad(menuwin, TRUE);
5186     box(menuwin, 0, 0);
5187
5188     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5189
5190     post_menu(m);
5191
5192     while ((c = menu_driver(m, menu_virtualize(wGetchar(menuwin)))) != E_UNKNOWN_COMMAND) {
5193         if (c == E_NOT_POSTED)
5194             break;
5195         if (c == E_REQUEST_DENIED)
5196             beep();
5197         continue;
5198     }
5199
5200     (void) mvprintw(LINES - 2, 0,
5201                     "You chose: %s\n", item_name(current_item(m)));
5202     (void) addstr("Press any key to continue...");
5203     wGetchar(stdscr);
5204
5205     unpost_menu(m);
5206     delwin(menuwin);
5207
5208     free_menu(m);
5209     for (ip = items; *ip; ip++)
5210         free_item(*ip);
5211 #ifdef NCURSES_MOUSE_VERSION
5212     mousemask(0, (mmask_t *) 0);
5213 #endif
5214 }
5215
5216 #ifdef TRACE
5217 #define T_TBL(name) { #name, name }
5218 static struct {
5219     const char *name;
5220     unsigned mask;
5221 } t_tbl[] = {
5222
5223     T_TBL(TRACE_DISABLE),
5224         T_TBL(TRACE_TIMES),
5225         T_TBL(TRACE_TPUTS),
5226         T_TBL(TRACE_UPDATE),
5227         T_TBL(TRACE_MOVE),
5228         T_TBL(TRACE_CHARPUT),
5229         T_TBL(TRACE_ORDINARY),
5230         T_TBL(TRACE_CALLS),
5231         T_TBL(TRACE_VIRTPUT),
5232         T_TBL(TRACE_IEVENT),
5233         T_TBL(TRACE_BITS),
5234         T_TBL(TRACE_ICALLS),
5235         T_TBL(TRACE_CCALLS),
5236         T_TBL(TRACE_DATABASE),
5237         T_TBL(TRACE_ATTRS),
5238         T_TBL(TRACE_MAXIMUM),
5239     {
5240         (char *) 0, 0
5241     }
5242 };
5243
5244 static char *
5245 tracetrace(unsigned tlevel)
5246 {
5247     static char *buf;
5248     int n;
5249
5250     if (buf == 0) {
5251         size_t need = 12;
5252         for (n = 0; t_tbl[n].name != 0; n++)
5253             need += strlen(t_tbl[n].name) + 2;
5254         buf = typeMalloc(char, need);
5255     }
5256     sprintf(buf, "0x%02x = {", tlevel);
5257     if (tlevel == 0) {
5258         sprintf(buf + strlen(buf), "%s, ", t_tbl[0].name);
5259     } else {
5260         for (n = 1; t_tbl[n].name != 0; n++)
5261             if ((tlevel & t_tbl[n].mask) == t_tbl[n].mask) {
5262                 strcat(buf, t_tbl[n].name);
5263                 strcat(buf, ", ");
5264             }
5265     }
5266     if (buf[strlen(buf) - 2] == ',')
5267         buf[strlen(buf) - 2] = '\0';
5268     return (strcat(buf, "}"));
5269 }
5270
5271 /* fake a dynamically reconfigurable menu using the 0th entry to deselect
5272  * the others
5273  */
5274 static int
5275 run_trace_menu(MENU * m)
5276 {
5277     ITEM **items;
5278     ITEM *i, **p;
5279
5280     for (;;) {
5281         bool changed = FALSE;
5282         switch (menu_driver(m, menu_virtualize(wGetchar(menu_win(m))))) {
5283         case E_UNKNOWN_COMMAND:
5284             return FALSE;
5285         default:
5286             items = menu_items(m);
5287             i = current_item(m);
5288             if (i == items[0]) {
5289                 if (item_value(i)) {
5290                     for (p = items + 1; *p != 0; p++)
5291                         if (item_value(*p)) {
5292                             set_item_value(*p, FALSE);
5293                             changed = TRUE;
5294                         }
5295                 }
5296             } else {
5297                 for (p = items + 1; *p != 0; p++)
5298                     if (item_value(*p)) {
5299                         set_item_value(items[0], FALSE);
5300                         changed = TRUE;
5301                         break;
5302                     }
5303             }
5304             if (!changed)
5305                 return TRUE;
5306         }
5307     }
5308 }
5309
5310 static void
5311 trace_set(void)
5312 /* interactively set the trace level */
5313 {
5314     MENU *m;
5315     ITEM *items[SIZEOF(t_tbl)];
5316     ITEM **ip = items;
5317     int mrows, mcols;
5318     unsigned newtrace;
5319     int n;
5320     WINDOW *menuwin;
5321
5322     mvaddstr(0, 0, "Interactively set trace level:");
5323     mvaddstr(2, 0, "  Press space bar to toggle a selection.");
5324     mvaddstr(3, 0, "  Use up and down arrow to move the select bar.");
5325     mvaddstr(4, 0, "  Press return to set the trace level.");
5326     mvprintw(6, 0, "(Current trace level is %s)", tracetrace(_nc_tracing));
5327
5328     refresh();
5329
5330     for (n = 0; t_tbl[n].name != 0; n++) {
5331         if ((*ip = new_item(t_tbl[n].name, "")) != 0) {
5332             ++ip;
5333         }
5334     }
5335     *ip = (ITEM *) 0;
5336
5337     m = new_menu(items);
5338
5339     set_menu_format(m, 0, 2);
5340     scale_menu(m, &mrows, &mcols);
5341
5342     menu_opts_off(m, O_ONEVALUE);
5343     menuwin = newwin(mrows + 2, mcols + 2, MENU_Y, MENU_X);
5344     set_menu_win(m, menuwin);
5345     keypad(menuwin, TRUE);
5346     box(menuwin, 0, 0);
5347
5348     set_menu_sub(m, derwin(menuwin, mrows, mcols, 1, 1));
5349
5350     post_menu(m);
5351
5352     for (ip = menu_items(m); *ip; ip++) {
5353         unsigned mask = t_tbl[item_index(*ip)].mask;
5354         if (mask == 0)
5355             set_item_value(*ip, _nc_tracing == 0);
5356         else if ((mask & _nc_tracing) == mask)
5357             set_item_value(*ip, TRUE);
5358     }
5359
5360     while (run_trace_menu(m))
5361         continue;
5362
5363     newtrace = 0;
5364     for (ip = menu_items(m); *ip; ip++)
5365         if (item_value(*ip))
5366             newtrace |= t_tbl[item_index(*ip)].mask;
5367     trace(newtrace);
5368     Trace(("trace level interactively set to %s", tracetrace(_nc_tracing)));
5369
5370     (void) mvprintw(LINES - 2, 0,
5371                     "Trace level is %s\n", tracetrace(_nc_tracing));
5372     (void) addstr("Press any key to continue...");
5373     wGetchar(stdscr);
5374
5375     unpost_menu(m);
5376     delwin(menuwin);
5377
5378     free_menu(m);
5379     for (ip = items; *ip; ip++)
5380         free_item(*ip);
5381 }
5382 #endif /* TRACE */
5383 #endif /* USE_LIBMENU */
5384
5385 /****************************************************************************
5386  *
5387  * Forms test
5388  *
5389  ****************************************************************************/
5390 #if USE_LIBFORM
5391 static FIELD *
5392 make_label(int frow, int fcol, NCURSES_CONST char *label)
5393 {
5394     FIELD *f = new_field(1, (int) strlen(label), frow, fcol, 0, 0);
5395
5396     if (f) {
5397         set_field_buffer(f, 0, label);
5398         set_field_opts(f, (int) (field_opts(f) & ~O_ACTIVE));
5399     }
5400     return (f);
5401 }
5402
5403 static FIELD *
5404 make_field(int frow, int fcol, int rows, int cols, bool secure)
5405 {
5406     FIELD *f = new_field(rows, cols, frow, fcol, 0, secure ? 1 : 0);
5407
5408     if (f) {
5409         set_field_back(f, A_UNDERLINE);
5410         set_field_userptr(f, (void *) 0);
5411     }
5412     return (f);
5413 }
5414
5415 static void
5416 display_form(FORM * f)
5417 {
5418     WINDOW *w;
5419     int rows, cols;
5420
5421     scale_form(f, &rows, &cols);
5422
5423     if ((w = newwin(rows + 2, cols + 4, 0, 0)) != (WINDOW *) 0) {
5424         set_form_win(f, w);
5425         set_form_sub(f, derwin(w, rows, cols, 1, 2));
5426         box(w, 0, 0);
5427         keypad(w, TRUE);
5428     }
5429
5430     if (post_form(f) != E_OK)
5431         wrefresh(w);
5432 }
5433
5434 static void
5435 erase_form(FORM * f)
5436 {
5437     WINDOW *w = form_win(f);
5438     WINDOW *s = form_sub(f);
5439
5440     unpost_form(f);
5441     werase(w);
5442     wrefresh(w);
5443     delwin(s);
5444     delwin(w);
5445 }
5446
5447 static int
5448 edit_secure(FIELD * me, int c)
5449 {
5450     int rows, cols, frow, fcol, nrow, nbuf;
5451
5452     if (field_info(me, &rows, &cols, &frow, &fcol, &nrow, &nbuf) == E_OK
5453         && nbuf > 0) {
5454         char *source = field_buffer(me, 1);
5455         char temp[80];
5456         long len;
5457
5458         strcpy(temp, source ? source : "");
5459         len = (long) (char *) field_userptr(me);
5460         if (c <= KEY_MAX) {
5461             if (isgraph(c) && (len + 1) < (int) sizeof(temp)) {
5462                 temp[len++] = (char) c;
5463                 temp[len] = 0;
5464                 set_field_buffer(me, 1, temp);
5465                 c = '*';
5466             } else {
5467                 c = 0;
5468             }
5469         } else {
5470             switch (c) {
5471             case REQ_BEG_FIELD:
5472             case REQ_CLR_EOF:
5473             case REQ_CLR_EOL:
5474             case REQ_DEL_LINE:
5475             case REQ_DEL_WORD:
5476             case REQ_DOWN_CHAR:
5477             case REQ_END_FIELD:
5478             case REQ_INS_CHAR:
5479             case REQ_INS_LINE:
5480             case REQ_LEFT_CHAR:
5481             case REQ_NEW_LINE:
5482             case REQ_NEXT_WORD:
5483             case REQ_PREV_WORD:
5484             case REQ_RIGHT_CHAR:
5485             case REQ_UP_CHAR:
5486                 c = 0;          /* we don't want to do inline editing */
5487                 break;
5488             case REQ_CLR_FIELD:
5489                 if (len) {
5490                     temp[0] = 0;
5491                     set_field_buffer(me, 1, temp);
5492                 }
5493                 break;
5494             case REQ_DEL_CHAR:
5495             case REQ_DEL_PREV:
5496                 if (len) {
5497                     temp[--len] = 0;
5498                     set_field_buffer(me, 1, temp);
5499                 }
5500                 break;
5501             }
5502         }
5503         set_field_userptr(me, (void *) len);
5504     }
5505     return c;
5506 }
5507
5508 static int
5509 form_virtualize(FORM * f, WINDOW *w)
5510 {
5511     /* *INDENT-OFF* */
5512     static const struct {
5513         int code;
5514         int result;
5515     } lookup[] = {
5516         { CTRL('A'),    REQ_NEXT_CHOICE },
5517         { CTRL('B'),    REQ_PREV_WORD },
5518         { CTRL('C'),    REQ_CLR_EOL },
5519         { CTRL('D'),    REQ_DOWN_FIELD },
5520         { CTRL('E'),    REQ_END_FIELD },
5521         { CTRL('F'),    REQ_NEXT_PAGE },
5522         { CTRL('G'),    REQ_DEL_WORD },
5523         { CTRL('H'),    REQ_DEL_PREV },
5524         { CTRL('I'),    REQ_INS_CHAR },
5525         { CTRL('K'),    REQ_CLR_EOF },
5526         { CTRL('L'),    REQ_LEFT_FIELD },
5527         { CTRL('M'),    REQ_NEW_LINE },
5528         { CTRL('N'),    REQ_NEXT_FIELD },
5529         { CTRL('O'),    REQ_INS_LINE },
5530         { CTRL('P'),    REQ_PREV_FIELD },
5531         { CTRL('R'),    REQ_RIGHT_FIELD },
5532         { CTRL('S'),    REQ_BEG_FIELD },
5533         { CTRL('U'),    REQ_UP_FIELD },
5534         { CTRL('V'),    REQ_DEL_CHAR },
5535         { CTRL('W'),    REQ_NEXT_WORD },
5536         { CTRL('X'),    REQ_CLR_FIELD },
5537         { CTRL('Y'),    REQ_DEL_LINE },
5538         { CTRL('Z'),    REQ_PREV_CHOICE },
5539         { ESCAPE,       MAX_FORM_COMMAND + 1 },
5540         { KEY_BACKSPACE, REQ_DEL_PREV },
5541         { KEY_DOWN,     REQ_DOWN_CHAR },
5542         { KEY_END,      REQ_LAST_FIELD },
5543         { KEY_HOME,     REQ_FIRST_FIELD },
5544         { KEY_LEFT,     REQ_LEFT_CHAR },
5545         { KEY_LL,       REQ_LAST_FIELD },
5546         { KEY_NEXT,     REQ_NEXT_FIELD },
5547         { KEY_NPAGE,    REQ_NEXT_PAGE },
5548         { KEY_PPAGE,    REQ_PREV_PAGE },
5549         { KEY_PREVIOUS, REQ_PREV_FIELD },
5550         { KEY_RIGHT,    REQ_RIGHT_CHAR },
5551         { KEY_UP,       REQ_UP_CHAR },
5552         { QUIT,         MAX_FORM_COMMAND + 1 }
5553     };
5554     /* *INDENT-ON* */
5555
5556     static int mode = REQ_INS_MODE;
5557     int c = wGetchar(w);
5558     unsigned n;
5559     FIELD *me = current_field(f);
5560     bool current = TRUE;
5561
5562     if (c == CTRL(']')) {
5563         if (mode == REQ_INS_MODE) {
5564             mode = REQ_OVL_MODE;
5565         } else {
5566             mode = REQ_INS_MODE;
5567         }
5568         c = mode;
5569     } else {
5570         for (n = 0; n < SIZEOF(lookup); n++) {
5571             if (lookup[n].code == c) {
5572                 c = lookup[n].result;
5573                 break;
5574             }
5575         }
5576     }
5577     mvprintw(0, COLS - 6, "(%s)", mode == REQ_INS_MODE ? "INS" : "OVL");
5578
5579     /*
5580      * Force the field that the user is typing into to be in reverse video,
5581      * while the other fields are shown underlined.
5582      */
5583     switch (c) {
5584     case REQ_BEG_FIELD:
5585     case REQ_CLR_EOF:
5586     case REQ_CLR_EOL:
5587     case REQ_CLR_FIELD:
5588     case REQ_DEL_CHAR:
5589     case REQ_DEL_LINE:
5590     case REQ_DEL_PREV:
5591     case REQ_DEL_WORD:
5592     case REQ_END_FIELD:
5593     case REQ_INS_CHAR:
5594     case REQ_INS_LINE:
5595     case REQ_LEFT_CHAR:
5596     case REQ_LEFT_FIELD:
5597     case REQ_NEXT_WORD:
5598     case REQ_RIGHT_CHAR:
5599         current = TRUE;
5600         break;
5601     default:
5602         current = (c < KEY_MAX);
5603         break;
5604     }
5605     if (current) {
5606         c = edit_secure(me, c);
5607         set_field_back(me, A_REVERSE);
5608     } else {
5609         c = edit_secure(me, c);
5610         set_field_back(me, A_UNDERLINE);
5611     }
5612     return c;
5613 }
5614
5615 static int
5616 my_form_driver(FORM * form, int c)
5617 {
5618     if (c == (MAX_FORM_COMMAND + 1)
5619         && form_driver(form, REQ_VALIDATION) == E_OK)
5620         return (TRUE);
5621     else {
5622         beep();
5623         return (FALSE);
5624     }
5625 }
5626
5627 #ifdef NCURSES_VERSION
5628 #define FIELDCHECK_CB(func) bool func(FIELD * fld, const void * data GCC_UNUSED)
5629 #define CHAR_CHECK_CB(func) bool func(int ch, const void *data GCC_UNUSED)
5630 #else
5631 #define FIELDCHECK_CB(func) int func(FIELD * fld, char * data GCC_UNUSED)
5632 #define CHAR_CHECK_CB(func) int func(int ch, char *data GCC_UNUSED)
5633 #endif
5634
5635 /*
5636  * Allow a middle initial, optionally with a '.' to end it.
5637  */
5638 static
5639 FIELDCHECK_CB(mi_field_check)
5640 {
5641     char *s = field_buffer(fld, 0);
5642     int state = 0;
5643     int n;
5644
5645     for (n = 0; s[n] != '\0'; ++n) {
5646         switch (state) {
5647         case 0:
5648             if (s[n] == '.') {
5649                 if (n != 1)
5650                     return FALSE;
5651                 state = 2;
5652             } else if (isspace(UChar(s[n]))) {
5653                 state = 2;
5654             }
5655             break;
5656         case 2:
5657             if (!isspace(UChar(s[n])))
5658                 return FALSE;
5659             break;
5660         }
5661     }
5662
5663     /* force the form to display a leading capital */
5664     if (islower(UChar(s[0]))) {
5665         s[0] = (char) toupper(UChar(s[0]));
5666         set_field_buffer(fld, 0, s);
5667     }
5668     return TRUE;
5669 }
5670
5671 static
5672 CHAR_CHECK_CB(mi_char_check)
5673 {
5674     return ((isalpha(ch) || ch == '.') ? TRUE : FALSE);
5675 }
5676
5677 /*
5678  * Passwords should be at least 6 characters.
5679  */
5680 static
5681 FIELDCHECK_CB(pw_field_check)
5682 {
5683     char *s = field_buffer(fld, 0);
5684     int n;
5685
5686     for (n = 0; s[n] != '\0'; ++n) {
5687         if (isspace(UChar(s[n]))) {
5688             if (n < 6)
5689                 return FALSE;
5690         }
5691     }
5692     return TRUE;
5693 }
5694
5695 static
5696 CHAR_CHECK_CB(pw_char_check)
5697 {
5698     return (isgraph(ch) ? TRUE : FALSE);
5699 }
5700
5701 static void
5702 demo_forms(void)
5703 {
5704     WINDOW *w;
5705     FORM *form;
5706     FIELD *f[12], *secure;
5707     FIELDTYPE *fty_middle = new_fieldtype(mi_field_check, mi_char_check);
5708     FIELDTYPE *fty_passwd = new_fieldtype(pw_field_check, pw_char_check);
5709     int finished = 0, c;
5710     unsigned n = 0;
5711
5712 #ifdef NCURSES_MOUSE_VERSION
5713     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5714 #endif
5715
5716     move(18, 0);
5717     addstr("Defined edit/traversal keys:   ^Q/ESC- exit form\n");
5718     addstr("^N   -- go to next field       ^P  -- go to previous field\n");
5719     addstr("Home -- go to first field      End -- go to last field\n");
5720     addstr("^L   -- go to field to left    ^R  -- go to field to right\n");
5721     addstr("^U   -- move upward to field   ^D  -- move downward to field\n");
5722     addstr("^W   -- go to next word        ^B  -- go to previous word\n");
5723     addstr("^S   -- go to start of field   ^E  -- go to end of field\n");
5724     addstr("^H   -- delete previous char   ^Y  -- delete line\n");
5725     addstr("^G   -- delete current word    ^C  -- clear to end of line\n");
5726     addstr("^K   -- clear to end of field  ^X  -- clear field\n");
5727     addstr("Arrow keys move within a field as you would expect. ^] toggles overlay mode.");
5728
5729     mvaddstr(4, 57, "Forms Entry Test");
5730
5731     refresh();
5732
5733     /* describe the form */
5734     memset(f, 0, sizeof(f));
5735     f[n++] = make_label(0, 15, "Sample Form");
5736
5737     f[n++] = make_label(2, 0, "Last Name");
5738     f[n++] = make_field(3, 0, 1, 18, FALSE);
5739     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5740
5741     f[n++] = make_label(2, 20, "First Name");
5742     f[n++] = make_field(3, 20, 1, 12, FALSE);
5743     set_field_type(f[n - 1], TYPE_ALPHA, 1);
5744
5745     f[n++] = make_label(2, 34, "Middle Name");
5746     f[n++] = make_field(3, 34, 1, 12, FALSE);
5747     set_field_type(f[n - 1], fty_middle);
5748
5749     f[n++] = make_label(5, 0, "Comments");
5750     f[n++] = make_field(6, 0, 4, 46, FALSE);
5751
5752     f[n++] = make_label(5, 20, "Password:");
5753     secure =
5754         f[n++] = make_field(5, 30, 1, 9, TRUE);
5755     set_field_type(f[n - 1], fty_passwd);
5756     f[n++] = (FIELD *) 0;
5757
5758     if ((form = new_form(f)) != 0) {
5759
5760         display_form(form);
5761
5762         w = form_win(form);
5763         raw();
5764         nonl();                 /* lets us read ^M's */
5765         while (!finished) {
5766             switch (form_driver(form, c = form_virtualize(form, w))) {
5767             case E_OK:
5768                 mvaddstr(5, 57, field_buffer(secure, 1));
5769                 clrtoeol();
5770                 refresh();
5771                 break;
5772             case E_UNKNOWN_COMMAND:
5773                 finished = my_form_driver(form, c);
5774                 break;
5775             default:
5776                 beep();
5777                 break;
5778             }
5779         }
5780
5781         erase_form(form);
5782
5783         free_form(form);
5784     }
5785     for (c = 0; f[c] != 0; c++)
5786         free_field(f[c]);
5787     free_fieldtype(fty_middle);
5788     free_fieldtype(fty_passwd);
5789     noraw();
5790     nl();
5791
5792 #ifdef NCURSES_MOUSE_VERSION
5793     mousemask(ALL_MOUSE_EVENTS, (mmask_t *) 0);
5794 #endif
5795 }
5796 #endif /* USE_LIBFORM */
5797
5798 /****************************************************************************
5799  *
5800  * Overlap test
5801  *
5802  ****************************************************************************/
5803
5804 static void
5805 fillwin(WINDOW *win, char ch)
5806 {
5807     int y, x;
5808     int y1, x1;
5809
5810     getmaxyx(win, y1, x1);
5811     for (y = 0; y < y1; y++) {
5812         wmove(win, y, 0);
5813         for (x = 0; x < x1; x++)
5814             waddch(win, UChar(ch));
5815     }
5816 }
5817
5818 static void
5819 crosswin(WINDOW *win, char ch)
5820 {
5821     int y, x;
5822     int y1, x1;
5823
5824     getmaxyx(win, y1, x1);
5825     for (y = 0; y < y1; y++) {
5826         for (x = 0; x < x1; x++)
5827             if (((x > (x1 - 1) / 3) && (x <= (2 * (x1 - 1)) / 3))
5828                 || (((y > (y1 - 1) / 3) && (y <= (2 * (y1 - 1)) / 3)))) {
5829                 wmove(win, y, x);
5830                 waddch(win, UChar(ch));
5831             }
5832     }
5833 }
5834
5835 #define OVERLAP_FLAVORS 5
5836
5837 static void
5838 overlap_helpitem(int state, int item, char *message)
5839 {
5840     int row = (item / 2);
5841     int col = ((item % 2) ? COLS / 2 : 0);
5842
5843     move(LINES - 6 + row, col);
5844     printw("%c%c = %s", state == row ? '>' : ' ', 'a' + item, message);
5845     clrtoeol();
5846 }
5847
5848 static void
5849 overlap_test_1_attr(WINDOW *win, int flavor, int col)
5850 {
5851     short cpair = (short) (1 + (flavor * 2) + col);
5852
5853     switch (flavor) {
5854     case 0:
5855         wattrset(win, A_NORMAL);
5856         break;
5857     case 1:
5858         wattrset(win, A_BOLD);
5859         break;
5860     case 2:
5861         init_pair(cpair, COLOR_BLUE, COLOR_WHITE);
5862         wattrset(win, COLOR_PAIR(cpair) | A_NORMAL);
5863         break;
5864     case 3:
5865         init_pair(cpair, COLOR_WHITE, COLOR_BLUE);
5866         wattrset(win, COLOR_PAIR(cpair) | A_BOLD);
5867         break;
5868     }
5869 }
5870
5871 static void
5872 overlap_test_2_attr(WINDOW *win, int flavor, int col)
5873 {
5874     short cpair = (short) (9 + (flavor * 2) + col);
5875
5876     switch (flavor) {
5877     case 0:
5878         /* no effect */
5879         break;
5880     case 1:
5881         /* no effect */
5882         break;
5883     case 2:
5884         init_pair(cpair, COLOR_RED, COLOR_GREEN);
5885         wbkgdset(win, colored_chtype(' ', A_BLINK, cpair));
5886         break;
5887     case 3:
5888         wbkgdset(win, ' ' | A_NORMAL);
5889         break;
5890     }
5891 }
5892
5893 static int
5894 overlap_help(int state, int flavors[OVERLAP_FLAVORS])
5895 {
5896     int row;
5897     int col;
5898     int item;
5899     const char *ths, *tht;
5900     char msg[80];
5901
5902     if (state < 0)
5903         state += OVERLAP_FLAVORS;
5904     state = state % OVERLAP_FLAVORS;
5905     assert(state >= 0 && state < OVERLAP_FLAVORS);
5906
5907     for (item = 0; item < (2 * OVERLAP_FLAVORS); ++item) {
5908         row = item / 2;
5909         col = item % 2;
5910         ths = col ? "B" : "A";
5911         tht = col ? "A" : "B";
5912
5913         switch (row) {
5914         case 0:
5915             flavors[row] = 0;
5916             sprintf(msg, "refresh %s, then %s, then doupdate.", ths, tht);
5917             break;
5918         case 1:
5919             if (use_colors) {
5920                 flavors[row] %= 4;
5921             } else {
5922                 flavors[row] %= 2;
5923             }
5924             overlap_test_1_attr(stdscr, flavors[row], col);
5925             sprintf(msg, "fill window %s with letter %s.", ths, ths);
5926             break;
5927         case 2:
5928             if (use_colors) {
5929                 flavors[row] %= 4;
5930             } else {
5931                 flavors[row] %= 2;
5932             }
5933             switch (flavors[row]) {
5934             case 0:
5935                 sprintf(msg, "cross pattern in window %s.", ths);
5936                 break;
5937             case 1:
5938                 sprintf(msg, "draw box in window %s.", ths);
5939                 break;
5940             case 2:
5941                 sprintf(msg, "set background of window %s.", ths);
5942                 break;
5943             case 3:
5944                 sprintf(msg, "reset background of window %s.", ths);
5945                 break;
5946             }
5947             break;
5948         case 3:
5949             flavors[row] = 0;
5950             sprintf(msg, "clear window %s.", ths);
5951             break;
5952         case 4:
5953             flavors[row] %= 4;
5954             switch (flavors[row]) {
5955             case 0:
5956                 sprintf(msg, "overwrite %s onto %s.", ths, tht);
5957                 break;
5958             case 1:
5959                 sprintf(msg, "copywin(FALSE) %s onto %s.", ths, tht);
5960                 break;
5961             case 2:
5962                 sprintf(msg, "copywin(TRUE) %s onto %s.", ths, tht);
5963                 break;
5964             case 3:
5965                 sprintf(msg, "overlay %s onto %s.", ths, tht);
5966                 break;
5967             }
5968             break;
5969         }
5970         overlap_helpitem(state, item, msg);
5971         wattrset(stdscr, A_NORMAL);
5972         wbkgdset(stdscr, ' ' | A_NORMAL);
5973     }
5974     move(LINES - 1, 0);
5975     printw("^Q/ESC = terminate test.  Up/down/space select test variations (%d %d).",
5976            state, flavors[state]);
5977
5978     return state;
5979 }
5980
5981 static void
5982 overlap_test_0(WINDOW *a, WINDOW *b)
5983 {
5984     touchwin(a);
5985     touchwin(b);
5986     wnoutrefresh(a);
5987     wnoutrefresh(b);
5988     doupdate();
5989 }
5990
5991 static void
5992 overlap_test_1(int flavor, int col, WINDOW *a, char fill)
5993 {
5994     overlap_test_1_attr(a, flavor, col);
5995     fillwin(a, fill);
5996     wattrset(a, A_NORMAL);
5997 }
5998
5999 static void
6000 overlap_test_2(int flavor, int col, WINDOW *a, char fill)
6001 {
6002     overlap_test_2_attr(a, flavor, col);
6003     switch (flavor) {
6004     case 0:
6005         crosswin(a, fill);
6006         break;
6007     case 1:
6008         box(a, 0, 0);
6009         break;
6010     case 2:
6011         /* done in overlap_test_2_attr */
6012         break;
6013     case 3:
6014         /* done in overlap_test_2_attr */
6015         break;
6016     }
6017 }
6018
6019 static void
6020 overlap_test_3(WINDOW *a)
6021 {
6022     wclear(a);
6023     wmove(a, 0, 0);
6024 }
6025
6026 static void
6027 overlap_test_4(int flavor, WINDOW *a, WINDOW *b)
6028 {
6029     switch (flavor) {
6030     case 0:
6031         overwrite(a, b);
6032         break;
6033     case 1:
6034         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), FALSE);
6035         break;
6036     case 2:
6037         copywin(a, b, 0, 0, 0, 0, getmaxy(b), getmaxx(b), TRUE);
6038         break;
6039     case 3:
6040         overlay(a, b);
6041         break;
6042     }
6043 }
6044
6045 /* test effects of overlapping windows */
6046 static void
6047 overlap_test(void)
6048 {
6049     int ch;
6050     int state, flavor[OVERLAP_FLAVORS];
6051
6052     WINDOW *win1 = newwin(9, 20, 3, 3);
6053     WINDOW *win2 = newwin(9, 20, 9, 16);
6054
6055     curs_set(0);
6056     raw();
6057     refresh();
6058     move(0, 0);
6059     printw("This test shows the behavior of wnoutrefresh() with respect to\n");
6060     printw("the shared region of two overlapping windows A and B.  The cross\n");
6061     printw("pattern in each window does not overlap the other.\n");
6062
6063     memset(flavor, 0, sizeof(flavor));
6064     state = overlap_help(0, flavor);
6065
6066     while (!isQuit(ch = Getchar()))
6067         switch (ch) {
6068         case 'a':               /* refresh window A first, then B */
6069             overlap_test_0(win1, win2);
6070             break;
6071
6072         case 'b':               /* refresh window B first, then A */
6073             overlap_test_0(win2, win1);
6074             break;
6075
6076         case 'c':               /* fill window A so it's visible */
6077             overlap_test_1(flavor[1], 0, win1, 'A');
6078             break;
6079
6080         case 'd':               /* fill window B so it's visible */
6081             overlap_test_1(flavor[1], 1, win2, 'B');
6082             break;
6083
6084         case 'e':               /* cross test pattern in window A */
6085             overlap_test_2(flavor[2], 0, win1, 'A');
6086             break;
6087
6088         case 'f':               /* cross test pattern in window A */
6089             overlap_test_2(flavor[2], 1, win2, 'B');
6090             break;
6091
6092         case 'g':               /* clear window A */
6093             overlap_test_3(win1);
6094             break;
6095
6096         case 'h':               /* clear window B */
6097             overlap_test_3(win2);
6098             break;
6099
6100         case 'i':               /* overwrite A onto B */
6101             overlap_test_4(flavor[4], win1, win2);
6102             break;
6103
6104         case 'j':               /* overwrite B onto A */
6105             overlap_test_4(flavor[4], win2, win1);
6106             break;
6107
6108         case CTRL('n'):
6109         case KEY_DOWN:
6110             state = overlap_help(state + 1, flavor);
6111             break;
6112
6113         case CTRL('p'):
6114         case KEY_UP:
6115             state = overlap_help(state - 1, flavor);
6116             break;
6117
6118         case ' ':
6119             flavor[state] += 1;
6120             state = overlap_help(state, flavor);
6121             break;
6122
6123         case '?':
6124             state = overlap_help(state, flavor);
6125             break;
6126
6127         default:
6128             beep();
6129             break;
6130         }
6131
6132     delwin(win2);
6133     delwin(win1);
6134     erase();
6135     curs_set(1);
6136     endwin();
6137 }
6138
6139 /****************************************************************************
6140  *
6141  * Main sequence
6142  *
6143  ****************************************************************************/
6144
6145 static bool
6146 do_single_test(const char c)
6147 /* perform a single specified test */
6148 {
6149     switch (c) {
6150     case 'a':
6151         getch_test();
6152         break;
6153
6154 #if USE_WIDEC_SUPPORT
6155     case 'A':
6156         get_wch_test();
6157         break;
6158 #endif
6159
6160     case 'b':
6161         attr_test();
6162         break;
6163
6164 #if USE_WIDEC_SUPPORT
6165     case 'B':
6166         wide_attr_test();
6167         break;
6168 #endif
6169
6170     case 'c':
6171         if (!use_colors)
6172             Cannot("does not support color.");
6173         else
6174             color_test();
6175         break;
6176
6177 #if USE_WIDEC_SUPPORT
6178     case 'C':
6179         if (!use_colors)
6180             Cannot("does not support color.");
6181         else
6182             wide_color_test();
6183         break;
6184 #endif
6185
6186     case 'd':
6187         if (!use_colors)
6188             Cannot("does not support color.");
6189         else if (!can_change_color())
6190             Cannot("has hardwired color values.");
6191         else
6192             color_edit();
6193         break;
6194
6195 #if USE_SOFTKEYS
6196     case 'e':
6197         slk_test();
6198         break;
6199 #endif
6200
6201 #if USE_WIDEC_SUPPORT
6202     case 'E':
6203         wide_slk_test();
6204         break;
6205 #endif
6206     case 'f':
6207         acs_display();
6208         break;
6209
6210 #if USE_WIDEC_SUPPORT
6211     case 'F':
6212         wide_acs_display();
6213         break;
6214 #endif
6215
6216 #if USE_LIBPANEL
6217     case 'o':
6218         demo_panels(init_panel, fill_panel);
6219         break;
6220 #endif
6221
6222 #if USE_WIDEC_SUPPORT && USE_LIBPANEL
6223     case 'O':
6224         demo_panels(init_wide_panel, fill_wide_panel);
6225         break;
6226 #endif
6227
6228     case 'g':
6229         acs_and_scroll();
6230         break;
6231
6232     case 'i':
6233         flushinp_test(stdscr);
6234         break;
6235
6236     case 'k':
6237         test_sgr_attributes();
6238         break;
6239
6240 #if USE_LIBMENU
6241     case 'm':
6242         menu_test();
6243         break;
6244 #endif
6245
6246     case 'p':
6247         demo_pad();
6248         break;
6249
6250 #if USE_LIBFORM
6251     case 'r':
6252         demo_forms();
6253         break;
6254 #endif
6255
6256     case 's':
6257         overlap_test();
6258         break;
6259
6260 #if USE_LIBMENU && defined(TRACE)
6261     case 't':
6262         trace_set();
6263         break;
6264 #endif
6265
6266     case '?':
6267         break;
6268
6269     default:
6270         return FALSE;
6271     }
6272
6273     return TRUE;
6274 }
6275
6276 static void
6277 usage(void)
6278 {
6279     static const char *const tbl[] =
6280     {
6281         "Usage: ncurses [options]"
6282         ,""
6283         ,"Options:"
6284 #ifdef NCURSES_VERSION
6285         ,"  -a f,b   set default-colors (assumed white-on-black)"
6286         ,"  -d       use default-colors if terminal supports them"
6287 #endif
6288 #if USE_SOFTKEYS
6289         ,"  -e fmt   specify format for soft-keys test (e)"
6290 #endif
6291 #if HAVE_RIPOFFLINE
6292         ,"  -f       rip-off footer line (can repeat)"
6293         ,"  -h       rip-off header line (can repeat)"
6294 #endif
6295         ,"  -m       do not use colors"
6296         ,"  -p file  rgb values to use in 'd' rather than ncurses's builtin"
6297 #if USE_LIBPANEL
6298         ,"  -s msec  specify nominal time for panel-demo (default: 1, to hold)"
6299 #endif
6300 #ifdef TRACE
6301         ,"  -t mask  specify default trace-level (may toggle with ^T)"
6302 #endif
6303     };
6304     size_t n;
6305     for (n = 0; n < SIZEOF(tbl); n++)
6306         fprintf(stderr, "%s\n", tbl[n]);
6307     ExitProgram(EXIT_FAILURE);
6308 }
6309
6310 static void
6311 set_terminal_modes(void)
6312 {
6313     noraw();
6314     cbreak();
6315     noecho();
6316     scrollok(stdscr, TRUE);
6317     idlok(stdscr, TRUE);
6318     keypad(stdscr, TRUE);
6319 }
6320
6321 #ifdef SIGUSR1
6322 static RETSIGTYPE
6323 announce_sig(int sig)
6324 {
6325     (void) fprintf(stderr, "Handled signal %d\r\n", sig);
6326 }
6327 #endif
6328
6329 #if HAVE_RIPOFFLINE
6330 static int
6331 rip_footer(WINDOW *win, int cols)
6332 {
6333     wbkgd(win, A_REVERSE);
6334     werase(win);
6335     wmove(win, 0, 0);
6336     wprintw(win, "footer: window %p, %d columns", win, cols);
6337     wnoutrefresh(win);
6338     return OK;
6339 }
6340
6341 static int
6342 rip_header(WINDOW *win, int cols)
6343 {
6344     wbkgd(win, A_REVERSE);
6345     werase(win);
6346     wmove(win, 0, 0);
6347     wprintw(win, "header: window %p, %d columns", win, cols);
6348     wnoutrefresh(win);
6349     return OK;
6350 }
6351 #endif /* HAVE_RIPOFFLINE */
6352
6353 static void
6354 main_menu(bool top)
6355 {
6356     char command;
6357
6358     do {
6359         (void) puts("This is the ncurses main menu");
6360         (void) puts("a = keyboard and mouse input test");
6361 #if USE_WIDEC_SUPPORT
6362         (void) puts("A = wide-character keyboard and mouse input test");
6363 #endif
6364         (void) puts("b = character attribute test");
6365 #if USE_WIDEC_SUPPORT
6366         (void) puts("B = wide-character attribute test");
6367 #endif
6368         (void) puts("c = color test pattern");
6369 #if USE_WIDEC_SUPPORT
6370         (void) puts("C = color test pattern using wide-character calls");
6371 #endif
6372         if (top)
6373             (void) puts("d = edit RGB color values");
6374 #if USE_SOFTKEYS
6375         (void) puts("e = exercise soft keys");
6376 #if USE_WIDEC_SUPPORT
6377         (void) puts("E = exercise soft keys using wide-characters");
6378 #endif
6379 #endif
6380         (void) puts("f = display ACS characters");
6381 #if USE_WIDEC_SUPPORT
6382         (void) puts("F = display Wide-ACS characters");
6383 #endif
6384         (void) puts("g = display windows and scrolling");
6385         (void) puts("i = test of flushinp()");
6386         (void) puts("k = display character attributes");
6387 #if USE_LIBMENU
6388         (void) puts("m = menu code test");
6389 #endif
6390 #if USE_LIBPANEL
6391         (void) puts("o = exercise panels library");
6392 #if USE_WIDEC_SUPPORT
6393         (void) puts("O = exercise panels with wide-characters");
6394 #endif
6395 #endif
6396         (void) puts("p = exercise pad features");
6397         (void) puts("q = quit");
6398 #if USE_LIBFORM
6399         (void) puts("r = exercise forms code");
6400 #endif
6401         (void) puts("s = overlapping-refresh test");
6402 #if USE_LIBMENU && defined(TRACE)
6403         (void) puts("t = set trace level");
6404 #endif
6405         (void) puts("? = repeat this command summary");
6406
6407         (void) fputs("> ", stdout);
6408         (void) fflush(stdout);  /* necessary under SVr4 curses */
6409
6410         /*
6411          * This used to be an 'fgets()' call.  However (on Linux, at least)
6412          * mixing stream I/O and 'read()' (used in the library) causes the
6413          * input stream to be flushed when switching between the two.
6414          */
6415         command = 0;
6416         for (;;) {
6417             char ch = '\0';
6418             if (read(fileno(stdin), &ch, 1) <= 0) {
6419                 if (command == 0)
6420                     command = 'q';
6421                 break;
6422             } else if (command == 0 && !isspace(UChar(ch))) {
6423                 command = ch;
6424             } else if (ch == '\n' || ch == '\r') {
6425                 if ((command == 'd') && !top) {
6426                     (void) fputs("Do not nest test-d\n", stdout);
6427                     command = 0;
6428                 }
6429                 if (command != 0)
6430                     break;
6431                 (void) fputs("> ", stdout);
6432                 (void) fflush(stdout);
6433             }
6434         }
6435
6436         if (do_single_test(command)) {
6437             /*
6438              * This may be overkill; it's intended to reset everything back
6439              * to the initial terminal modes so that tests don't get in
6440              * each other's way.
6441              */
6442             flushinp();
6443             set_terminal_modes();
6444             reset_prog_mode();
6445             clear();
6446             refresh();
6447             endwin();
6448             if (command == '?') {
6449                 (void) puts("This is the ncurses capability tester.");
6450                 (void)
6451                     puts("You may select a test from the main menu by typing the");
6452                 (void)
6453                     puts("key letter of the choice (the letter to left of the =)");
6454                 (void)
6455                     puts("at the > prompt.  Type `q' to exit.");
6456             }
6457             continue;
6458         }
6459     } while
6460         (command != 'q');
6461 }
6462
6463 /*+-------------------------------------------------------------------------
6464         main(argc,argv)
6465 --------------------------------------------------------------------------*/
6466
6467 #define okCOLOR(n) ((n) >= 0 && (n) < max_colors)
6468 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
6469
6470 int
6471 main(int argc, char *argv[])
6472 {
6473     int c;
6474     int my_e_param = 1;
6475 #ifdef NCURSES_VERSION
6476     int default_fg = COLOR_WHITE;
6477     int default_bg = COLOR_BLACK;
6478     bool assumed_colors = FALSE;
6479     bool default_colors = FALSE;
6480 #endif
6481     char *palette_file = 0;
6482     bool monochrome = FALSE;
6483
6484     setlocale(LC_ALL, "");
6485
6486     while ((c = getopt(argc, argv, "a:de:fhmp:s:t:")) != -1) {
6487         switch (c) {
6488 #ifdef NCURSES_VERSION
6489         case 'a':
6490             assumed_colors = TRUE;
6491             sscanf(optarg, "%d,%d", &default_fg, &default_bg);
6492             break;
6493         case 'd':
6494             default_colors = TRUE;
6495             break;
6496 #endif
6497         case 'e':
6498             my_e_param = atoi(optarg);
6499 #ifdef NCURSES_VERSION
6500             if (my_e_param > 3) /* allow extended layouts */
6501                 usage();
6502 #else
6503             if (my_e_param > 1)
6504                 usage();
6505 #endif
6506             break;
6507 #if HAVE_RIPOFFLINE
6508         case 'f':
6509             ripoffline(-1, rip_footer);
6510             break;
6511         case 'h':
6512             ripoffline(1, rip_header);
6513             break;
6514 #endif /* HAVE_RIPOFFLINE */
6515         case 'm':
6516             monochrome = TRUE;
6517             break;
6518         case 'p':
6519             palette_file = optarg;
6520             break;
6521 #if USE_LIBPANEL
6522         case 's':
6523             nap_msec = atol(optarg);
6524             break;
6525 #endif
6526 #ifdef TRACE
6527         case 't':
6528             save_trace = (unsigned) strtol(optarg, 0, 0);
6529             break;
6530 #endif
6531         default:
6532             usage();
6533         }
6534     }
6535
6536     /*
6537      * If there's no menus (unlikely for ncurses!), then we'll have to set
6538      * tracing on initially, just in case the user wants to test something that
6539      * doesn't involve wGetchar.
6540      */
6541 #ifdef TRACE
6542     /* enable debugging */
6543 #if !USE_LIBMENU
6544     trace(save_trace);
6545 #else
6546     if (!isatty(fileno(stdin)))
6547         trace(save_trace);
6548 #endif /* USE_LIBMENU */
6549 #endif /* TRACE */
6550
6551 #if USE_SOFTKEYS
6552     /* tell it we're going to play with soft keys */
6553     slk_init(my_e_param);
6554 #endif
6555
6556 #ifdef SIGUSR1
6557     /* set up null signal catcher so we can see what interrupts to getch do */
6558     signal(SIGUSR1, announce_sig);
6559 #endif
6560
6561     /* we must initialize the curses data structure only once */
6562     initscr();
6563     bkgdset(BLANK);
6564
6565     /* tests, in general, will want these modes */
6566     use_colors = monochrome ? FALSE : has_colors();
6567
6568     if (use_colors) {
6569         start_color();
6570 #ifdef NCURSES_VERSION_PATCH
6571         max_colors = COLORS;    /* was > 16 ? 16 : COLORS */
6572 #if HAVE_USE_DEFAULT_COLORS
6573         if (default_colors) {
6574             use_default_colors();
6575             min_colors = -1;
6576         }
6577 #if NCURSES_VERSION_PATCH >= 20000708
6578         else if (assumed_colors)
6579             assume_default_colors(default_fg, default_bg);
6580 #endif
6581 #endif
6582 #else /* normal SVr4 curses */
6583         max_colors = COLORS;    /* was > 8 ? 8 : COLORS */
6584 #endif
6585         max_pairs = COLOR_PAIRS;        /* was > 256 ? 256 : COLOR_PAIRS */
6586
6587         if (can_change_color()) {
6588             short cp;
6589             all_colors = typeMalloc(RGB_DATA, (unsigned) max_colors);
6590             for (cp = 0; cp < max_colors; ++cp) {
6591                 color_content(cp,
6592                               &all_colors[cp].red,
6593                               &all_colors[cp].green,
6594                               &all_colors[cp].blue);
6595             }
6596             if (palette_file != 0) {
6597                 FILE *fp = fopen(palette_file, "r");
6598                 if (fp != 0) {
6599                     char buffer[BUFSIZ];
6600                     int red, green, blue;
6601                     int scale = 1000;
6602                     while (fgets(buffer, sizeof(buffer), fp) != 0) {
6603                         if (sscanf(buffer, "scale:%d", &c) == 1) {
6604                             scale = c;
6605                         } else if (sscanf(buffer, "%d:%d %d %d",
6606                                           &c,
6607                                           &red,
6608                                           &green,
6609                                           &blue) == 4
6610                                    && okCOLOR(c)
6611                                    && okRGB(red)
6612                                    && okRGB(green)
6613                                    && okRGB(blue)) {
6614                             all_colors[c].red = (short) ((red * 1000) / scale);
6615                             all_colors[c].green = (short) ((green * 1000) / scale);
6616                             all_colors[c].blue = (short) ((blue * 1000) / scale);
6617                         }
6618                     }
6619                     fclose(fp);
6620                 }
6621             }
6622         }
6623     }
6624     set_terminal_modes();
6625     def_prog_mode();
6626
6627     /*
6628      * Return to terminal mode, so we're guaranteed of being able to
6629      * select terminal commands even if the capabilities are wrong.
6630      */
6631     endwin();
6632
6633 #if HAVE_CURSES_VERSION
6634     (void) printf("Welcome to %s.  Press ? for help.\n", curses_version());
6635 #elif defined(NCURSES_VERSION_MAJOR) && defined(NCURSES_VERSION_MINOR) && defined(NCURSES_VERSION_PATCH)
6636     (void) printf("Welcome to ncurses %d.%d.%d.  Press ? for help.\n",
6637                   NCURSES_VERSION_MAJOR,
6638                   NCURSES_VERSION_MINOR,
6639                   NCURSES_VERSION_PATCH);
6640 #else
6641     (void) puts("Welcome to ncurses.  Press ? for help.");
6642 #endif
6643
6644     main_menu(TRUE);
6645
6646     ExitProgram(EXIT_SUCCESS);
6647 }
6648
6649 /* ncurses.c ends here */