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