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