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