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