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