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