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